From 720e833d58c8f151c823afc1d6fa16163597bd49 Mon Sep 17 00:00:00 2001 From: MacBlaze1 <33578623+MacBlaze1@users.noreply.github.com> Date: Thu, 17 Mar 2022 14:52:26 -0400 Subject: [PATCH] Allows more than two assemblies to be connected together (#65240) This PR changes how assemblies and assembly holders work, and allows you to connect more than just a signaler and an igniter together. You could have a signaler, a repeating timer, and an igniter together to constantly produce sparks. Making the assembly holder is the same as before, but now you can add assemblies to it. This also allows signalers to be triggered via right clicking them while holding them in your active hand. --- code/modules/assembly/assembly.dm | 4 + code/modules/assembly/bomb.dm | 6 +- code/modules/assembly/holder.dm | 149 +++++++++++++++--------------- code/modules/assembly/infrared.dm | 3 +- code/modules/assembly/signaler.dm | 13 +++ 5 files changed, 98 insertions(+), 77 deletions(-) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 54fe2d6943a..089f1e95a82 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -104,6 +104,10 @@ else to_chat(user, span_warning("Both devices must be in attachable mode to be attached together.")) 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) ..() /obj/item/assembly/screwdriver_act(mob/living/user, obj/item/I) diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index f2d504d5942..d033f862b88 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -115,7 +115,11 @@ //Bomb assembly proc. This turns assembly+tank into a bomb /obj/item/tank/proc/bomb_assemble(obj/item/assembly_holder/assembly, mob/living/user) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - if(isigniter(assembly.a_left) == isigniter(assembly.a_right)) + var/igniter_count = 0 + for(var/obj/item/assembly/attached_assembly as anything in assembly.assemblies) + if(isigniter(attached_assembly)) + igniter_count += 1 + if(LAZYLEN(assembly.assemblies) == igniter_count) return if((src in user.get_equipped_items(TRUE)) && !user.canUnEquip(src)) diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 6ea9b5f8511..32191f31da5 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -10,25 +10,20 @@ w_class = WEIGHT_CLASS_SMALL throw_speed = 2 throw_range = 7 - - var/obj/item/assembly/a_left = null - var/obj/item/assembly/a_right = null + /// used to store the list of assemblies making up our assembly holder + var/list/obj/item/assembly/assemblies /obj/item/assembly_holder/Initialize(mapload) . = ..() AddComponent(/datum/component/simple_rotation) /obj/item/assembly_holder/Destroy() - QDEL_NULL(a_left) - QDEL_NULL(a_right) + QDEL_LAZYLIST(assemblies) return ..() /obj/item/assembly_holder/Exited(atom/movable/gone, direction) . = ..() - if(gone == a_left) - a_left = null - else if(gone == a_right) - a_right = null + LAZYREMOVE(assemblies, gone) /obj/item/assembly_holder/IsAssemblyHolder() return TRUE @@ -41,6 +36,23 @@ update_appearance() SSblackbox.record_feedback("tally", "assembly_made", 1, "[initial(A.name)]-[initial(A2.name)]") +/** + * Adds an assembly to the assembly holder + * + * This proc is used to add an assembly to the assembly holder, update the appearance, and the name of it. + * Arguments: + * * attached_assembly - assembly we are adding to the assembly holder + * * user - user we pass into attach() + */ +/obj/item/assembly_holder/proc/add_assembly(obj/item/assembly/attached_assembly, mob/user) + attach(attached_assembly, user) + name = "" + for(var/obj/item/assembly/assembly as anything in assemblies) + name += "[assembly.name]-" + name = splicetext(name, length(name), length(name) + 1, "") + name += " assembly" + update_appearance() + /obj/item/assembly_holder/proc/attach(obj/item/assembly/A, mob/user) if(!A.remove_item_from_storage(src)) if(user) @@ -49,10 +61,7 @@ A.forceMove(src) A.holder = src A.toggle_secure() - if(!a_left) - a_left = A - else - a_right = A + LAZYADD(assemblies, A) A.holder_movement() A.on_attach() @@ -62,96 +71,88 @@ /obj/item/assembly_holder/update_overlays() . = ..() - if(a_left) - . += "[a_left.icon_state]_left" - for(var/left_overlay in a_left.attached_overlays) - . += "[left_overlay]_l" - - if(!a_right) - return - - if(a_right.is_position_sensitive) - . += "[a_right.icon_state]_right" - for(var/right_overlay in a_right.attached_overlays) - . += "[right_overlay]_r" - return - - var/mutable_appearance/right = mutable_appearance(icon, "[a_right.icon_state]_left") - right.transform = matrix(-1, 0, 0, 0, 1, 0) - for(var/right_overlay in a_right.attached_overlays) - right.add_overlay("[right_overlay]_l") - . += right + for(var/i in 1 to LAZYLEN(assemblies)) + if(i % 2 == 1) + var/obj/item/assembly/assembly = assemblies[i] + . += "[assembly.icon_state]_left" + for(var/left_overlay in assembly.attached_overlays) + . += "[left_overlay]_l" + if(i % 2 == 0) + var/obj/item/assembly/assembly = assemblies[i] + var/mutable_appearance/right = mutable_appearance(icon, "[assembly.icon_state]_left") + right.transform = matrix(-1, 0, 0, 0, 1, 0) + for(var/right_overlay in assembly.attached_overlays) + right.add_overlay("[right_overlay]_l") + . += right /obj/item/assembly_holder/on_found(mob/finder) - if(a_left) - a_left.on_found(finder) - if(a_right) - a_right.on_found(finder) + for(var/obj/item/assembly/assembly as anything in assemblies) + assembly.on_found(finder) /obj/item/assembly_holder/setDir() . = ..() - if(a_left) - a_left.holder_movement() - if(a_right) - a_right.holder_movement() + for(var/obj/item/assembly/assembly as anything in assemblies) + assembly.holder_movement() /obj/item/assembly_holder/dropped(mob/user) . = ..() - if(a_left) - a_left.dropped() - if(a_right) - a_right.dropped() + for(var/obj/item/assembly/assembly as anything in assemblies) + assembly.dropped() /obj/item/assembly_holder/attack_hand(mob/living/user, list/modifiers)//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess . = ..() if(.) return - if(a_left) - a_left.attack_hand() - if(a_right) - a_right.attack_hand() + for(var/obj/item/assembly/assembly as anything in assemblies) + assembly.attack_hand() +/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") + return + return ..() /obj/item/assembly_holder/AltClick(mob/user) return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool) if(..()) return TRUE - to_chat(user, span_notice("You disassemble [src]!")) - if(a_left) - a_left.on_detach() - a_left = null - if(a_right) - a_right.on_detach() - a_right = null + balloon_alert(user, "disassembled") + for(var/obj/item/assembly/assembly as anything in assemblies) + assembly.on_detach() + LAZYREMOVE(assemblies, assembly) qdel(src) return TRUE /obj/item/assembly_holder/attack_self(mob/user) src.add_fingerprint(user) - if(!a_left || !a_right) - to_chat(user, span_danger("Assembly part missing!")) + if(LAZYLEN(assemblies) == 1) + balloon_alert(user, "part missing!") return - if(istype(a_left,a_right.type))//If they are the same type it causes issues due to window code - switch(tgui_alert(usr,"Which side would you like to use?",,list("Left","Right"))) - if("Left") - a_left.attack_self(user) - if("Right") - a_right.attack_self(user) - return - else - a_left.attack_self(user) - a_right.attack_self(user) + for(var/obj/item/assembly/assembly as anything in assemblies) + assembly.attack_self(user) -/obj/item/assembly_holder/proc/process_activation(obj/D, normal = 1, special = 1) - if(!D) +/** + * this proc is used to process the activation of the assembly holder + * + * This proc is usually called by signalers, timers, or anything that can trigger and + * 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) + if(!device) return FALSE - if((normal) && (a_right) && (a_left)) - if(a_right != D) - a_right.pulsed(FALSE) - if(a_left != D) - a_left.pulsed(FALSE) + if(normal && LAZYLEN(assemblies) >= 2) + for(var/obj/item/assembly/assembly as anything in assemblies) + if(LAZYACCESS(assemblies, assembly) != device) + assembly.pulsed(FALSE) if(master) master.receive_signal() return TRUE diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index ca35ced6c6b..60f863176ff 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -101,8 +101,7 @@ for(var/i in 1 to maxlength) var/obj/effect/beam/i_beam/I = new(T) if(istype(holder, /obj/item/assembly_holder)) - var/obj/item/assembly_holder/assembly_holder = holder - I.icon_state = "[initial(I.icon_state)]_[(assembly_holder.a_left == src) ? "l":"r"]" //Sync the offset of the beam with the position of the sensor. + I.icon_state = "[initial(I.icon_state)]_l" //Sync the offset of the beam with the position of the sensor. else if(istype(holder, /obj/item/transfer_valve)) I.icon_state = "[initial(I.icon_state)]_ttv" I.set_density(TRUE) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 21886c1eb74..b148f51b408 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -123,6 +123,19 @@ to_chat(user, "You transfer the frequency and code of \the [signaler2.name] to \the [name]") ..() +/obj/item/assembly/signaler/attack_self_secondary(mob/user, modifiers) + . = ..() + if(!can_interact(user)) + return + if(!ishuman(user)) + return + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_SIGNALLER_SEND)) + balloon_alert(user, "still recharging...") + return + TIMER_COOLDOWN_START(src, COOLDOWN_SIGNALLER_SEND, 1 SECONDS) + INVOKE_ASYNC(src, .proc/signal) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + /obj/item/assembly/signaler/proc/signal() if(!radio_connection) return