Files
BatrachophrenoandGitHub c92d909534 Wires Panel and Signaler Expansion (#22846)
Fleshes out wire panel skill behavior and signaler management.

Electrical Engineering 4 was very OP for revealing all wire labels of
all machines, and it was super fun while it lasted.

Wire panels now have associated skills with their use, and increased
skill ranks will increase the proportion of wires (chosen at random)
that are easily recognizable on sight for certain types of wiring
panels.
**Professional:** 75% revealed
**Trained:** 50% revealed
**Familiar:** 30% revealed
_(note: having both Electrical and Mechanical engineering at least
Trained gives you that baseline 30%, reflecting how your knowledge can
probably translate to decent guesswork.)_

The following wire panels are affected:
**Electrical Engineering:**
- APCs
- Cameras
- IFF Beacons
- Radios
- Smartfridges
- SMES Units

**Mechanical Engineering**
- Airlocks
- Disposal Units
- Fabricators
- Vending Machines

**Atmospherics Systems**
- Air Alarms

**Reactor Systems**
- Particle Accelerator Control Boxes (lol)

**Robotics**
- Hardsuits
- Suit Storage Units (sharing the love is all)
- Robots
- Tag Scanners

**Xenobiology**
- Stasis Cages

Also, I made it possible to blow out all the lights in an APC's area by
snipping the power regulator cable and then pulsing the lights wire.
Great for signalers.

Speaking of signalers, new app deployed to all Engineering, Research,
and Robotics personal modular computers:
<img width="816" height="722" alt="Screenshot 2026-07-14 144049"
src="https://github.com/user-attachments/assets/3d588712-ebfd-47ac-b699-7cdac2c04907"
/>

Self-explanatory. Please only use corporate-provided software for
behavior within your contract regulations.
2026-07-26 18:05:15 +00:00

269 lines
7.1 KiB
Plaintext

/obj/item/assembly_holder
name = "assembly"
icon = 'icons/obj/assemblies/new_assemblies.dmi'
icon_state = "holder"
item_state = "assembly"
obj_flags = OBJ_FLAG_CONDUCTABLE
movable_flags = MOVABLE_FLAG_PROXMOVE
throwforce = 5
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 10
var/secured = FALSE
var/obj/item/assembly/a_left = null
var/obj/item/assembly/a_right = null
var/obj/special_assembly = null
/obj/item/assembly_holder/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(distance <= 1 || src.loc == user)
if (src.secured)
. += SPAN_NOTICE("\The [src] is ready!")
else
. += SPAN_NOTICE("\The [src] can be attached!")
/obj/item/assembly_holder/Initialize(mapload, ...)
. = ..()
become_hearing_sensitive()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/item/assembly_holder/Destroy()
lose_hearing_sensitivity()
QDEL_NULL(a_left)
QDEL_NULL(a_right)
QDEL_NULL(special_assembly)
. = ..()
/obj/item/assembly_holder/proc/detached()
if(a_left)
a_left.holder_movement()
if(a_right)
a_right.holder_movement()
return
/obj/item/assembly_holder/IsAssemblyHolder()
return TRUE
/obj/item/assembly_holder/proc/attach(var/obj/item/assembly/D, var/obj/item/assembly/D2, var/mob/user)
if(!istype(D) || !istype(D2))
return FALSE
if(D.secured || D2.secured)
return FALSE
if(user)
user.remove_from_mob(D)
user.remove_from_mob(D2)
D.holder = src
D2.holder = src
D.forceMove(src)
D2.forceMove(src)
a_left = D
a_right = D2
name = "[D.name]-[D2.name] assembly"
update_icon()
user.put_in_hands(src)
return TRUE
/obj/item/assembly_holder/proc/attach_special(var/obj/O, var/mob/user)
if(!O)
return
if(!O.IsSpecialAssembly())
return FALSE
/obj/item/assembly_holder/update_icon()
ClearOverlays()
if(a_left)
AddOverlays("[a_left.icon_state]_left")
for(var/O in a_left.attached_overlays)
AddOverlays("[O]_l")
if(a_right)
AddOverlays("[a_right.icon_state]_right")
for(var/O in a_right.attached_overlays)
AddOverlays("[O]_r")
if(master)
master.update_icon()
/obj/item/assembly_holder/HasProximity(atom/movable/AM as mob|obj)
if(a_left)
a_left.HasProximity(AM)
if(a_right)
a_right.HasProximity(AM)
if(special_assembly)
special_assembly.HasProximity(AM)
/obj/item/assembly_holder/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(a_left)
a_left.on_entered(source, arrived, old_loc, old_locs)
if(a_right)
a_right.on_entered(source, arrived, old_loc, old_locs)
/obj/item/assembly_holder/on_found(mob/finder)
if(a_left)
a_left.on_found(finder)
if(a_right)
a_right.on_found(finder)
if(special_assembly)
if(istype(special_assembly, /obj/item))
var/obj/item/S = special_assembly
S.on_found(finder)
/obj/item/assembly_holder/Move()
. = ..()
if(a_left && a_right)
a_left.holder_movement()
a_right.holder_movement()
/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
if(a_left && a_right)
a_left.holder_movement()
a_right.holder_movement()
return ..()
/obj/item/assembly_holder/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER)
if(!a_left || !a_right)
to_chat(user, SPAN_DANGER("BUG: Assembly part missing, please report this!"))
return
a_left.toggle_secure()
a_right.toggle_secure()
secured = !secured
if(secured)
to_chat(user, SPAN_NOTICE("\The [src] is ready!"))
else
to_chat(user, SPAN_NOTICE("\The [src] can now be taken apart!"))
update_icon()
return
else if(attacking_item.IsSpecialAssembly())
attach_special(attacking_item, user)
else
return ..()
/obj/item/assembly_holder/attack_self(mob/user)
add_fingerprint(user)
if(secured)
if(!a_left || !a_right)
to_chat(user, SPAN_WARNING("Assembly part missing!"))
return
if(istype(a_left, a_right.type)) //If they are the same type it causes issues due to window code
switch(alert("Which side would you like to use?",,"Left","Right"))
if("Left")
a_left.attack_self(user)
if("Right")
a_right.attack_self(user)
return
else
if(!istype(a_left, /obj/item/assembly/igniter))
a_left.attack_self(user)
if(!istype(a_right, /obj/item/assembly/igniter))
a_right.attack_self(user)
else
var/turf/T = get_turf(src)
if(!T)
return FALSE
if(a_left)
a_left.holder = null
a_left.forceMove(T)
a_left = null
if(a_right)
a_right.holder = null
a_right.forceMove(T)
a_right = null
qdel(src)
/obj/item/assembly_holder/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1)
if(!D)
return FALSE
if(!secured)
audible_message("[icon2html(src, viewers(get_turf(src)))] *beep* *beep*", "*beep* *beep*")
return FALSE
if(normal && a_right && a_left)
var/obj/item/assembly/right = a_right
if(right && right != D && right.holder == src)
right.pulsed(FALSE)
if(QDELETED(src))
return TRUE
var/obj/item/assembly/left = a_left
if(left && left != D && left.holder == src)
left.pulsed(FALSE)
if(QDELETED(src))
return TRUE
if(master)
master.receive_signal()
else
audible_message("[icon2html(src, viewers(get_turf(src)))] *beep* *beep*", "*beep* *beep*")
return TRUE
/obj/item/assembly_holder/hear_talk(mob/living/M, msg, verb, datum/language/speaking)
if(a_right)
a_right.hear_talk(M, msg, verb, speaking)
if(a_left)
a_left.hear_talk(M, msg, verb, speaking)
/obj/item/assembly_holder/timer_igniter
name = "timer-igniter assembly"
/obj/item/assembly_holder/timer_igniter/Initialize(mapload, ...)
. = ..()
var/obj/item/assembly/igniter/ign = new(src)
ign.secured = TRUE
ign.holder = src
var/obj/item/assembly/timer/tmr = new(src)
tmr.time = 5
tmr.secured = TRUE
tmr.holder = src
START_PROCESSING(SSprocessing, tmr)
a_left = tmr
a_right = ign
secured = TRUE
update_icon()
name = "[initial(name)] ([tmr.time] secs)"
loc.verbs += /obj/item/assembly_holder/timer_igniter/verb/configure
/obj/item/assembly_holder/timer_igniter/detached()
loc.verbs -= /obj/item/assembly_holder/timer_igniter/verb/configure
..()
/obj/item/assembly_holder/timer_igniter/verb/configure()
set name = "Set Timer"
set category = "Object.Held"
set src in usr
if(!(usr.stat || usr.restrained()))
var/obj/item/assembly_holder/holder
if(istype(src,/obj/item/grenade/chem_grenade))
var/obj/item/grenade/chem_grenade/gren = src
holder = gren.detonator
var/obj/item/assembly/timer/tmr = holder.a_left
if(!istype(tmr, /obj/item/assembly/timer))
tmr = holder.a_right
if(!istype(tmr, /obj/item/assembly/timer))
to_chat(usr, SPAN_WARNING("This detonator has no timer."))
return
if(tmr.timing)
to_chat(usr, SPAN_WARNING("The timer has already been activated."))
else
var/ntime = input("Enter desired time in seconds", "Time", "5") as num
ntime = clamp(ntime, 5, 1000)
tmr.time = ntime
name = "[initial(name)] ([tmr.time] secs)"
to_chat(usr, SPAN_NOTICE("Timer set to [tmr.time] seconds."))
else
to_chat(usr, SPAN_WARNING("You cannot do this while [usr.stat ? "unconscious/dead" : "restrained"]."))