diff --git a/aurorastation.dme b/aurorastation.dme
index 3cece0f186d..e45665d8632 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -719,6 +719,7 @@
#include "code\datums\wires\disposals.dm"
#include "code\datums\wires\explosive.dm"
#include "code\datums\wires\fabricator.dm"
+#include "code\datums\wires\hardsuit.dm"
#include "code\datums\wires\iff.dm"
#include "code\datums\wires\ipc_tag_scanner.dm"
#include "code\datums\wires\landmine.dm"
@@ -728,6 +729,7 @@
#include "code\datums\wires\robot.dm"
#include "code\datums\wires\smartfridge.dm"
#include "code\datums\wires\smes.dm"
+#include "code\datums\wires\stasis_cage.dm"
#include "code\datums\wires\suit_storage_unit.dm"
#include "code\datums\wires\vending.dm"
#include "code\datums\wires\wires.dm"
@@ -2155,7 +2157,6 @@
#include "code\modules\clothing\spacesuits\rig\rig_construction.dm"
#include "code\modules\clothing\spacesuits\rig\rig_pieces.dm"
#include "code\modules\clothing\spacesuits\rig\rig_verbs.dm"
-#include "code\modules\clothing\spacesuits\rig\rig_wiring.dm"
#include "code\modules\clothing\spacesuits\rig\modules\combat.dm"
#include "code\modules\clothing\spacesuits\rig\modules\computer.dm"
#include "code\modules\clothing\spacesuits\rig\modules\modules.dm"
@@ -3139,6 +3140,7 @@
#include "code\modules\modular_computers\file_system\programs\generic\ntnrc_client.dm"
#include "code\modules\modular_computers\file_system\programs\generic\records.dm"
#include "code\modules\modular_computers\file_system\programs\generic\scanner.dm"
+#include "code\modules\modular_computers\file_system\programs\generic\signaler.dm"
#include "code\modules\modular_computers\file_system\programs\medical\suit_sensors.dm"
#include "code\modules\modular_computers\file_system\programs\pai\access_lock.dm"
#include "code\modules\modular_computers\file_system\programs\pai\directives.dm"
diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm
index c111a952f4d..39fea6220db 100644
--- a/code/__DEFINES/wires.dm
+++ b/code/__DEFINES/wires.dm
@@ -61,9 +61,6 @@
#define WIRE_TIMING "Timing"
#define WIRE_TX "Transmit"
#define WIRE_UNBOLT "Unbolt"
-#define WIRE_ZAP "High Voltage Circuit"
-#define WIRE_ZAP1 "High Voltage Circuit 1"
-#define WIRE_ZAP2 "High Voltage Circuit 2"
#define WIRE_OVERCLOCK "Overclock"
#define WIRE_FOCUS "Focus"
#define WIRE_FLUSH "Flush"
diff --git a/code/datums/skills/occupational/engineering.dm b/code/datums/skills/occupational/engineering.dm
index 5b71002fa55..9cb68dc6dad 100644
--- a/code/datums/skills/occupational/engineering.dm
+++ b/code/datums/skills/occupational/engineering.dm
@@ -10,10 +10,13 @@
skill_level_descriptions = alist(
SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with electrical systems.",
SKILL_LEVEL_FAMILIAR = "You have some experience working with electrical systems, though are not formally trained.
" \
- + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.",
- SKILL_LEVEL_TRAINED = "You have formal training in electrical engineering concepts, equivalent to a Bachelor's Degree.",
+ + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.
" \
+ + " - You can recognize what a few wires do in electrical devices by sight.",
+ SKILL_LEVEL_TRAINED = "You have formal training in electrical engineering concepts, equivalent to a Bachelor's Degree.
" \
+ + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.
" \
+ + " - You can recognize what several wires do in electrical devices by sight.",
SKILL_LEVEL_PROFESSIONAL = "You have many years of training in electrical related Engineering concepts, equivalent to a Master's Degree or better.
" \
- + " - You can tell what most machinery and airlock wires do just by looking at them." \
+ + " - You can recognize what many wires do in electrical devices by sight." \
)
/singleton/skill/mechanical_engineering
@@ -35,14 +38,17 @@
SKILL_LEVEL_FAMILIAR = "You have some experience working with physical machinery, though are not formally trained.
" \
+ " - Items you craft will be of slightly lower than average quality.
" \
+ " - You take 50% longer to craft anything.
" \
- + " - You can interact with some Engineering-related machinery.",
+ + " - You can interact with some Engineering-related machinery.
" \
+ + " - You can recognize what a few wires do in mechanical devices by sight.",
SKILL_LEVEL_TRAINED = "You have formal training in general Engineering concepts, equivalent to a Bachelor's Degree.
" \
+ " - You can craft items at the standard speed.
" \
- + " - Items you craft are generally at average quality.
" \
- + " - You can interact with most if not all Engineering equipment.",
+ + " - You can craft a wider variety of items.
" \
+ + " - You can interact with most if not all Engineering equipment.
" \
+ + " - You can recognize what several wires do in mechanical devices by sight.",
SKILL_LEVEL_PROFESSIONAL = "You have many years of training in general Engineering concepts, equivalent to a Master's Degree or better.
" \
+ " - You can craft items 50% faster.
" \
- + " - Items you craft are of much higher quality on average.
" \
+ + " - You can craft a much wider variety of items.
" \
+ + " - You can recognize what many wires do in mechanical devices by sight.",
)
/singleton/skill/atmospherics_systems
@@ -54,6 +60,16 @@
subcategory = SKILL_SUBCATEGORY_ENGINEERING
required = TRUE
component_type = ATMOSPHERICS_SYSTEMS_SKILL_COMPONENT
+ skill_level_descriptions = alist(
+ SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with atmospherics systems.
" \
+ + " - No mechanical changes implemented at this skill level.",
+ SKILL_LEVEL_FAMILIAR = "You have some experience working with atmospherics systems, though are not formally trained.
" \
+ + " - You can recognize what a few wires do in atmospherics devices by sight.",
+ SKILL_LEVEL_TRAINED = "You have formal training in general Engineering concepts, equivalent to a Bachelor's Degree.
" \
+ + " - You can recognize what some wires do in atmospherics devices by sight.",
+ SKILL_LEVEL_PROFESSIONAL = "You have many years of training in general Engineering concepts, equivalent to a Master's Degree or better.
" \
+ + " - You can recognize what many wires do in atmospherics devices by sight." \
+ )
/singleton/skill/reactor_systems
name = "Reactor Systems"
@@ -66,9 +82,9 @@
required = TRUE
component_type = REACTOR_SYSTEMS_SKILL_COMPONENT
skill_level_descriptions = alist(
- SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with Reactor systems.
" \
+ SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with reactor systems.
" \
+ " - You cannot interact with Reactor computers such as the INDRA controllers.",
- SKILL_LEVEL_FAMILIAR = "You have some experience working with physical machinery, though are not formally trained.
" \
+ SKILL_LEVEL_FAMILIAR = "You have some experience working with reactor systems, though are not formally trained.
" \
+ " - You cannot interact with Reactor computers such as the INDRA controller.",
SKILL_LEVEL_TRAINED = "You have formal training in general Engineering concepts, equivalent to a Bachelor's Degree.
" \
+ " - This is the minimum skill level required to operate the INDRA.",
diff --git a/code/datums/skills/occupational/operations.dm b/code/datums/skills/occupational/operations.dm
index 87502d3855e..10a6edd4a8b 100644
--- a/code/datums/skills/occupational/operations.dm
+++ b/code/datums/skills/occupational/operations.dm
@@ -15,12 +15,14 @@
SKILL_LEVEL_UNFAMILIAR = "You have zero training or experience with synthetics.
" \
+ " - You suffer a large penalty to the chances of successfully performing any surgery on synthetics.",
SKILL_LEVEL_FAMILIAR = "You have minimal training on the basics of synthetic repair and maintenance. This could be the level of a hobbyist, or someone currently pursuing a degree in robotics.
" \
+ + " - You can recognize what a few wires do in mechatronic devices by sight.
" \
+ "You can perform the following procedures without penalties:
" \
+ " - Opening or closing external maintenance panels to make superficial repairs.
" \
+ " - Repairing basic damage with a welder or cables.
" \
+ " - Repairing external damage to mechanical limbs.
" \
+ " - Cutting someone out of a hardsuit.",
SKILL_LEVEL_TRAINED = "You have years of formal training or experience on repairing and maintaining synthetics equivalent to a Bachelor's degree in Robotics.
" \
+ + " - You can recognize what several wires do in mechatronic devices by sight.
" \
+ "You can perform the following procedures without penalties:
" \
+ " - Opening or closing external maintenance panels to make superficial repairs.
" \
+ " - Repairing basic damage with a welder or cables.
" \
@@ -33,7 +35,8 @@
+ " - Re-attach (organic) limbs. Robotic limbs require the Robotics skill instead.
" \
+ " - Perform all forms of internal repairs to IPCs.
" \
+ " - Prepare an MMI for cyborgification.",
- SKILL_LEVEL_PROFESSIONAL = "Not currently implemented, functions exactly as per Trained.
" \
+ SKILL_LEVEL_PROFESSIONAL = "You have years of formal training or experience on repairing and maintaining synthetics equivalent to a Master's degree in Robotics or better.
" \
+ + " - You can recognize what many wires do in mechatronic devices by sight.
" \
+ "You can perform the following procedures without penalties:
" \
+ " - Opening or closing external maintenance panels to make superficial repairs.
" \
+ " - Repairing basic damage with a welder or cables.
" \
diff --git a/code/datums/skills/occupational/science.dm b/code/datums/skills/occupational/science.dm
index 11ba41e11ac..9adf9ffec53 100644
--- a/code/datums/skills/occupational/science.dm
+++ b/code/datums/skills/occupational/science.dm
@@ -39,16 +39,19 @@
skill_level_descriptions = alist(
SKILL_LEVEL_UNFAMILIAR = "You have little to no knowledge of Xenobiology",
SKILL_LEVEL_FAMILIAR = "You have a passing experience with Xenobiology.
" \
+ + " - You can recognize what a few wires do in xenobiological sciences hardware by sight.
" \
+ " - Your melee attacks made against Xenofauna such as Space Carp deal 10% more damage.
" \
+ " - You harvest one additional part when butchering Xenofauna.
" \
+ " - You are +2.5% more likely to succeed when performing surgery on a patient that is a different species than your own.
" \
+ " - You can perform surgeries 5% faster on a patient that is a different species than your own.",
SKILL_LEVEL_TRAINED = "You have a moderate experience with Xenobiology.
" \
+ + " - You can recognize what several wires do in xenobiological sciences hardware by sight.
" \
+ " - Your melee attacks made against Xenofauna such as Space Carp deal 15% more damage.
" \
+ " - You harvest two additional parts when butchering Xenofauna.
" \
+ " - You are +5% more likely to succeed when performing surgery on a patient that is a different species than your own.
" \
+ " - You can perform surgeries 10% faster on a patient that is a different species than your own.",
SKILL_LEVEL_PROFESSIONAL = "You have extensive experience with Xenobiology.
" \
+ + " - You can recognize what many wires do in xenobiological sciences hardware by sight.
" \
+ " - Your melee attacks made against Xenofauna such as Space Carp deal 20% more damage.
" \
+ " - You harvest three additional parts when butchering Xenofauna.
" \
+ " - You are +7.5% more likely to succeed when performing surgery on a patient that is a different species than your own.
" \
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 1bd05d1fd9d..0ebda75935a 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -3,6 +3,7 @@
/datum/wires/airlock
proper_name = "Airlock"
holder_type = /obj/structure/machinery/door/airlock
+ associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT
/datum/wires/airlock/New(atom/holder)
wires = list(
@@ -200,6 +201,6 @@
continue
dat += "[capitalize(color)]: [get_wire(color)]
"
- var/datum/browser/wire_win = new(user, "airlockwires", "Airlock Wires", 450, 500)
+ var/datum/browser/wire_win = new(user, "airlockwires", "Airlock Wires", 450, 720)
wire_win.set_content(dat)
wire_win.open()
diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm
index ac75f7c0057..edfe7fbcdaa 100644
--- a/code/datums/wires/alarm.dm
+++ b/code/datums/wires/alarm.dm
@@ -1,8 +1,9 @@
/datum/wires/alarm
proper_name = "Air Alarm"
holder_type = /obj/structure/machinery/alarm
+ associated_skill = ATMOSPHERICS_SYSTEMS_SKILL_COMPONENT
-/datum/wires/airalarm/New(atom/holder)
+/datum/wires/alarm/New(atom/holder)
wires = list(
WIRE_POWER,
WIRE_IDSCAN, WIRE_AI,
@@ -12,7 +13,7 @@
add_duds(3)
..()
-/datum/wires/airalarm/interactable(mob/user)
+/datum/wires/alarm/interactable(mob/user)
if(!..())
return FALSE
var/obj/structure/machinery/alarm/A = holder
@@ -23,7 +24,7 @@
var/obj/structure/machinery/alarm/A = holder
. = ..()
. += A.locked ? "The air alarm is locked." : "The air alarm is unlocked."
- . += (A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The Air Alarm is offline." : "The Air Alarm is working properly!"
+ . += (A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The air alarm is offline." : "The air alarm is working properly!"
. += A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on."
. += A.rcon_setting ? "The remote control link is enabled." : "The remote control link is disabled."
diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm
index 7350c4f762b..bcf7441b395 100644
--- a/code/datums/wires/apc.dm
+++ b/code/datums/wires/apc.dm
@@ -5,7 +5,8 @@
/datum/wires/apc/New(atom/holder)
wires = list(
WIRE_POWER1, WIRE_POWER2,
- WIRE_IDSCAN, WIRE_AI
+ WIRE_IDSCAN, WIRE_AI,
+ WIRE_POWER_LIMIT, WIRE_LIGHT
)
add_duds(6)
..()
@@ -14,8 +15,9 @@
var/obj/structure/machinery/power/apc/A = holder
. = ..()
. += A.locked ? "The APC is locked." : "The APC is unlocked."
- . += A.shorted ? "The APCs power has been shorted." : "The APC is working properly!"
+ . += A.shorted ? "The APCs power has been shorted!" : "The APC is working properly."
. += A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on."
+ . += A.light_explosion_safety ? "The low-voltage regulator telltale is on." : "The low-voltage regulator telltale is off."
/datum/wires/apc/interactable(mob/user)
if(!..())
@@ -24,23 +26,21 @@
return A?.panel_open
/datum/wires/apc/on_pulse(wire)
-
var/obj/structure/machinery/power/apc/A = holder
switch(wire)
-
if(WIRE_IDSCAN)
set_locked(A, FALSE)
addtimer(CALLBACK(src, PROC_REF(set_locked), A, TRUE), 30 SECONDS)
-
- if (WIRE_POWER1, WIRE_POWER2)
+ if(WIRE_POWER1, WIRE_POWER2)
set_short_out(A, TRUE)
addtimer(CALLBACK(src, PROC_REF(set_short_out), A, FALSE), 120 SECONDS)
-
- if (WIRE_AI)
+ if(WIRE_AI)
set_ai_control(A, TRUE)
addtimer(CALLBACK(src, PROC_REF(set_ai_control), A, FALSE), 1 SECONDS)
-
+ if(WIRE_LIGHT)
+ if(!A.light_explosion_safety)
+ A.overload_lighting()
/datum/wires/apc/proc/set_locked(var/obj/structure/machinery/power/apc/A, var/setting)
if(A)
@@ -74,3 +74,6 @@
if(WIRE_AI)
A.aidisabled = !mend
+
+ if(WIRE_POWER_LIMIT)
+ A.light_explosion_safety = mend
diff --git a/code/datums/wires/disposals.dm b/code/datums/wires/disposals.dm
index 1e7ff3b104a..8cd44742d36 100644
--- a/code/datums/wires/disposals.dm
+++ b/code/datums/wires/disposals.dm
@@ -1,7 +1,7 @@
/datum/wires/disposal
proper_name = "Disposals"
- random = TRUE
holder_type = /obj/structure/machinery/disposal
+ associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT
/datum/wires/disposal/New(atom/holder)
wires = list(
diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm
index db992864312..cc4c41f762c 100644
--- a/code/datums/wires/explosive.dm
+++ b/code/datums/wires/explosive.dm
@@ -1,3 +1,7 @@
+/datum/wires/explosive
+ // If we ever get an explosives or similar skill...
+ associated_skill = null
+
/datum/wires/explosive/New()
wires = list(
WIRE_EXPLODE
diff --git a/code/datums/wires/fabricator.dm b/code/datums/wires/fabricator.dm
index 84679d06562..a6f9d5563a1 100644
--- a/code/datums/wires/fabricator.dm
+++ b/code/datums/wires/fabricator.dm
@@ -1,6 +1,7 @@
/datum/wires/fabricator
proper_name = "Fabricator"
holder_type = /obj/structure/machinery/fabricator
+ associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT
/datum/wires/fabricator/New(atom/holder)
wires = list(
diff --git a/code/modules/clothing/spacesuits/rig/rig_wiring.dm b/code/datums/wires/hardsuit.dm
similarity index 98%
rename from code/modules/clothing/spacesuits/rig/rig_wiring.dm
rename to code/datums/wires/hardsuit.dm
index b545e7cb9b1..396c80110ba 100644
--- a/code/modules/clothing/spacesuits/rig/rig_wiring.dm
+++ b/code/datums/wires/hardsuit.dm
@@ -11,8 +11,8 @@
#define RIG_INTERFACE_SHOCK BITFLAG(5)
/datum/wires/rig
- random = 1
holder_type = /obj/item/rig
+ associated_skill = ROBOTICS_SKILL_COMPONENT
/datum/wires/rig/New(atom/holder)
wires = list(
diff --git a/code/datums/wires/ipc_tag_scanner.dm b/code/datums/wires/ipc_tag_scanner.dm
index a812d9674e0..9e0703fbeb6 100644
--- a/code/datums/wires/ipc_tag_scanner.dm
+++ b/code/datums/wires/ipc_tag_scanner.dm
@@ -1,7 +1,7 @@
/datum/wires/tag_scanner
proper_name = "IPC Tag Scanner"
- random = TRUE
holder_type = /obj/item/ipc_tag_scanner
+ associated_skill = ROBOTICS_SKILL_COMPONENT
/datum/wires/tag_scanner/New()
wires = list(
diff --git a/code/datums/wires/landmine.dm b/code/datums/wires/landmine.dm
index 51a4dde9f65..3142af09866 100644
--- a/code/datums/wires/landmine.dm
+++ b/code/datums/wires/landmine.dm
@@ -1,3 +1,7 @@
+/datum/wires/landmine
+ // If we ever get an explosives or similar skill...
+ associated_skill = null
+
/datum/wires/landmine/New()
wires = list(
WIRE_EXPLODE
diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm
index f9fc2ffe8d5..2d7d1a3358c 100644
--- a/code/datums/wires/nuclearbomb.dm
+++ b/code/datums/wires/nuclearbomb.dm
@@ -2,6 +2,8 @@
proper_name = "Nuclear Fission Device"
holder_type = /obj/structure/machinery/nuclearbomb
random = 1
+ // If we ever get an explosives or similar skill... well, probably not even then.
+ associated_skill = null
/datum/wires/nuclearbomb/New()
wires = list(
diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm
index bdcd4e32a9e..c3f42171144 100644
--- a/code/datums/wires/particle_accelerator.dm
+++ b/code/datums/wires/particle_accelerator.dm
@@ -1,6 +1,7 @@
/datum/wires/particle_acc/control_box
proper_name = "Particle Accelerator"
holder_type = /obj/structure/machinery/particle_accelerator/control_box
+ associated_skill = REACTOR_SYSTEMS_SKILL_COMPONENT
/datum/wires/particle_acc/control_box/New()
wires = list(
diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm
index 3a9043cdf86..0c8682102c7 100644
--- a/code/datums/wires/robot.dm
+++ b/code/datums/wires/robot.dm
@@ -1,7 +1,7 @@
/datum/wires/robot
proper_name = "Synthetic"
- random = TRUE
holder_type = /mob/living/silicon/robot
+ associated_skill = ROBOTICS_SKILL_COMPONENT
/datum/wires/robot/New()
wires = list(
diff --git a/code/datums/wires/stasis_cage.dm b/code/datums/wires/stasis_cage.dm
new file mode 100644
index 00000000000..8f7f10263e3
--- /dev/null
+++ b/code/datums/wires/stasis_cage.dm
@@ -0,0 +1,56 @@
+/datum/wires/stasis_cage
+ proper_name = "Stasis Cage"
+ holder_type = /obj/structure/machinery/stasis_cage
+ associated_skill = XENOBIOLOGY_SKILL_COMPONENT
+
+/datum/wires/stasis_cage/New(atom/holder)
+ wires = list(
+ WIRE_SAFETY,
+ WIRE_RELEASE,
+ WIRE_LOCK
+ )
+ add_duds(3)
+ ..()
+
+/datum/wires/stasis_cage/interactable(mob/user)
+ SHOULD_CALL_PARENT(FALSE)
+ var/obj/structure/machinery/stasis_cage/stasis_cage = holder
+ if (stasis_cage.panel_open)
+ return TRUE
+ return FALSE
+
+/datum/wires/stasis_cage/get_status()
+ var/obj/structure/machinery/stasis_cage/stasis_cage = holder
+ . = ..()
+ if(!stasis_cage.use_power)
+ . += "The panel is unpowered."
+ else
+ . += "The panel is powered."
+ . += "The biometric safety sensors are [(WIRE_SAFETY in cut_wires) ? "connected" : "disconnected"]."
+ . += "The cage's emergency auto-release mechanism is [(WIRE_RELEASE in cut_wires) ? "disabled" : "enabled"]."
+ . += "The cage lid motors are [(WIRE_LOCK in cut_wires) ? "overriden" : "nominal"]."
+
+/datum/wires/stasis_cage/on_cut(wire, mend, source)
+ var/obj/structure/machinery/stasis_cage/stasis_cage = holder
+ switch (wire)
+ if (WIRE_SAFETY)
+ stasis_cage.safety = !stasis_cage.safety
+ if (WIRE_RELEASE)
+ if (stasis_cage.contained && !mend)
+ stasis_cage.release()
+ holder.update_icon()
+ if (WIRE_LOCK)
+ if (!mend)
+ playsound(stasis_cage.loc, 'sound/machines/BoltsDown.ogg', 60)
+ holder.visible_message(SPAN_WARNING("The cage's lid bolts down destructively, denting itself!"), SPAN_NOTICE("You notice the cage lid override flag blink hastily."))
+ stasis_cage.broken = TRUE
+ holder.update_icon()
+
+/datum/wires/stasis_cage/on_pulse(wire)
+ var/obj/structure/machinery/stasis_cage/stasis_cage = holder
+ switch (wire)
+ if (WIRE_SAFETY)
+ if (stasis_cage.contained)
+ if (prob(20))
+ holder.visible_message(SPAN_WARNING("The cage hastily flicks open its lid!"), SPAN_NOTICE("You notice the biometric sensor flag blink fervently."))
+ stasis_cage.release()
diff --git a/code/datums/wires/suit_storage_unit.dm b/code/datums/wires/suit_storage_unit.dm
index b462f83d544..d7e03a12d45 100644
--- a/code/datums/wires/suit_storage_unit.dm
+++ b/code/datums/wires/suit_storage_unit.dm
@@ -1,6 +1,7 @@
/datum/wires/suit_storage_unit
proper_name = "Suit Storage Unit"
holder_type = /obj/structure/machinery/suit_cycler
+ associated_skill = ROBOTICS_SKILL_COMPONENT
/datum/wires/suit_storage_unit/New()
wires = list(
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index eb2d327d234..bd1eb532482 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -1,6 +1,7 @@
/datum/wires/vending
proper_name = "Vending Machine"
holder_type = /obj/structure/machinery/vending
+ associated_skill = MECHANICAL_ENGINEERING_SKILL_COMPONENT
/datum/wires/vending/New()
wires = list(
@@ -78,6 +79,6 @@
continue
dat += "[capitalize(color)]: [get_wire(color)]
"
- var/datum/browser/wire_win = new(user, "vendingwires", "Vending Wires", 450, 500)
+ var/datum/browser/wire_win = new(user, "vendingwires", "Vending Wires", 450, 720)
wire_win.set_content(dat)
wire_win.open()
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index c01ca21d33c..8bc46caa09d 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -2,6 +2,7 @@
GLOBAL_LIST_INIT(wire_color_directory, list())
GLOBAL_LIST_INIT(wire_name_directory, list())
+GLOBAL_LIST_INIT(wire_skill_reveal_directory, list())
/datum/wires
/// The holder (atom that contains these wires).
@@ -27,6 +28,10 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
var/random = FALSE
/// Wire manufacturer for the UI skin.
var/manufacturer = "hephaestus"
+ /// Skill component path used for partial wire reveals. Null disables skill-based reveals.
+ var/associated_skill = ELECTRICAL_ENGINEERING_SKILL_COMPONENT
+ /// Key used for round-stable partial skill-based reveals
+ var/skill_reveal_key
/datum/wires/New(atom/holder)
..()
@@ -40,6 +45,7 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
// If there is a dictionary key set, we'll want to use that. Otherwise, use the holder type.
var/key = dictionary_key ? dictionary_key : holder_type
+ skill_reveal_key = key
RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(on_holder_qdel))
if(random)
@@ -54,6 +60,7 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
/datum/wires/Destroy()
holder = null
+ skill_reveal_key = null
//properly clear refs to avoid harddels & other problems
for(var/color in assemblies)
var/obj/item/assembly/assembly = assemblies[color]
@@ -102,6 +109,8 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
/datum/wires/proc/shuffle_wires()
colors.Cut()
+ if(skill_reveal_key)
+ GLOB.wire_skill_reveal_directory -= skill_reveal_key
randomize()
/datum/wires/proc/repair()
@@ -241,8 +250,8 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
/**
* Checks whether wire assignments should be revealed.
*
- * Returns TRUE if the wires should be revealed, FALSE otherwise.
- * Currently checks for admin ghost AI, abductor multitool and blueprints.
+ * Returns TRUE if every wire assignment in this panel should be revealed, FALSE otherwise.
+ * Currently checks for admin ghosts and blueprints.
* Arguments:
* * user - The mob to check when deciding whether to reveal wires.
*/
@@ -259,10 +268,6 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
if(istype(user.get_active_hand(), /obj/item/blueprints))
return TRUE
- // Max electrical engineering skill grants this as a perk
- if (GET_SKILL_LEVEL(user, ELECTRICAL_ENGINEERING_SKILL_COMPONENT) >= SKILL_LEVEL_PROFESSIONAL)
- return TRUE
-
return FALSE
/**
@@ -276,6 +281,57 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
/datum/wires/proc/always_reveal_wire(color)
return FALSE
+/**
+ * Gets how many wires this user's associated skill reveals.
+ *
+ * Familiar: 15%; Trained: 35%; Professional: 65%.
+ * Arguments:
+ * * user - The mob to check when deciding how many wires to reveal.
+ */
+/datum/wires/proc/get_skill_revealed_wire_count(mob/user)
+ if(random || !associated_skill || !LAZYLEN(colors) || !user)
+ return 0
+
+ var/skill_level = GET_SKILL_LEVEL(user, associated_skill)
+ if(isnull(skill_level))
+ skill_level = SKILL_LEVEL_UNFAMILIAR
+
+ // If you're trained in various electromechanical biz, you can probably suss out at least some basics.
+ var/has_electromech_proficiency = (GET_SKILL_LEVEL(user, ELECTRICAL_ENGINEERING_SKILL_COMPONENT) >= SKILL_LEVEL_TRAINED && GET_SKILL_LEVEL(user, MECHANICAL_ENGINEERING_SKILL_COMPONENT) >= SKILL_LEVEL_TRAINED)
+ var/reveal_percent = 0
+ if(skill_level >= SKILL_LEVEL_PROFESSIONAL)
+ reveal_percent = 75
+ else if(skill_level >= SKILL_LEVEL_TRAINED)
+ reveal_percent = 50
+ else if(skill_level >= SKILL_LEVEL_FAMILIAR || has_electromech_proficiency)
+ reveal_percent = 30
+ else
+ return 0
+
+ return clamp(round(LAZYLEN(colors) * reveal_percent / 100), 0, LAZYLEN(colors))
+
+/datum/wires/proc/get_skill_revealed_colors(var/reveal_count)
+ if(reveal_count <= 0 || random || !LAZYLEN(colors))
+ return null
+
+ var/reveal_key = skill_reveal_key ? skill_reveal_key : (dictionary_key ? dictionary_key : holder_type)
+ var/list/skill_reveal_order = GLOB.wire_skill_reveal_directory[reveal_key]
+ var/rebuild_order = !skill_reveal_order || length(skill_reveal_order) != LAZYLEN(colors)
+ if(!rebuild_order)
+ for(var/color in skill_reveal_order)
+ if(!(color in colors))
+ rebuild_order = TRUE
+ break
+
+ if(rebuild_order)
+ skill_reveal_order = list()
+ for(var/color in colors)
+ skill_reveal_order += color
+ skill_reveal_order = shuffle(skill_reveal_order)
+ GLOB.wire_skill_reveal_directory[reveal_key] = skill_reveal_order
+
+ return skill_reveal_order.Copy(1, reveal_count + 1)
+
/datum/wires/ui_host()
return holder
@@ -297,11 +353,14 @@ GLOBAL_LIST_INIT(wire_name_directory, list())
/datum/wires/ui_data(mob/user)
var/list/data = list()
var/list/payload = list()
- var/reveal_wires = can_reveal_wires(user)
+ var/reveal_all = can_reveal_wires(user)
+ var/list/skill_revealed_colors
+ if(!reveal_all)
+ skill_revealed_colors = get_skill_revealed_colors(get_skill_revealed_wire_count(user))
for(var/color in colors)
var/wire_label = null
- if (reveal_wires || always_reveal_wire(color))
+ if(reveal_all || always_reveal_wire(color) || (skill_revealed_colors && (color in skill_revealed_colors)))
wire_label = is_dud_color(color) ? "None" : get_wire(color)
payload.Add(list(list(
diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm
index 40b5049fbd7..7c7b2f76dd7 100644
--- a/code/game/objects/items/devices/PDA/radio.dm
+++ b/code/game/objects/items/devices/PDA/radio.dm
@@ -44,8 +44,12 @@
var/frequency = 1457
var/code = 30.0
var/last_transmission
+ var/listening = TRUE
var/datum/radio_frequency/radio_connection
+/obj/item/integrated_signaler/signal/network_card
+ listening = FALSE
+
/obj/item/integrated_signaler/signal/Initialize()
. = ..()
if(!SSradio)
@@ -57,26 +61,83 @@
set_frequency(frequency)
/obj/item/integrated_signaler/signal/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- radio_connection = SSradio.add_object(src, frequency)
+ if(!SSradio || !new_frequency)
+ return FALSE
+ var/sanitized_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
+ if(sanitized_frequency > RADIO_HIGH_FREQ)
+ sanitized_frequency = RADIO_HIGH_FREQ - 1
+ if(radio_connection && frequency == sanitized_frequency)
+ return TRUE
+ if(radio_connection)
+ SSradio.remove_object(src, frequency)
+ frequency = sanitized_frequency
+ if(listening)
+ radio_connection = SSradio.add_object(src, frequency)
+ else
+ radio_connection = null
+ return TRUE
-/obj/item/integrated_signaler/signal/proc/send_signal(message="ACTIVATE")
+/obj/item/integrated_signaler/signal/proc/set_listening(var/new_listening)
+ new_listening = !!new_listening
+ if(listening == new_listening)
+ return TRUE
+
+ listening = new_listening
+ if(listening)
+ return set_frequency(frequency)
+
+ if(radio_connection)
+ SSradio.remove_object(src, frequency)
+ radio_connection = null
+ return TRUE
+
+/obj/item/integrated_signaler/signal/proc/send_signal(message="ACTIVATE", var/mob/user)
+ if(!SSradio)
+ return FALSE
+ var/datum/radio_frequency/transmit_connection = radio_connection
+ if(!transmit_connection)
+ transmit_connection = SSradio.return_frequency(frequency)
+ if(!transmit_connection)
+ return FALSE
+ if(within_jamming_range(src))
+ return FALSE
if(last_transmission && world.time < (last_transmission + 5))
- return
+ return FALSE
last_transmission = world.time
var/time = time2text(world.realtime,"hh:mm:ss")
var/turf/T = get_turf(src)
- GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")
+ var/user_key = user ? user.key : usr?.key
+ GLOB.lastsignalers.Add("[time] : [user_key ? user_key : "userless"] used [src] @ location ([T ? T.x : 0],[T ? T.y : 0],[T ? T.z : 0]) : [format_frequency(frequency)]/[code]")
var/datum/signal/signal = new
signal.source = src
signal.encryption = code
signal.data["message"] = message
+ if(user)
+ signal.data["user"] = WEAKREF(user)
- radio_connection.post_signal(src, signal)
+ transmit_connection.post_signal(src, signal)
+ return TRUE
+
+/obj/item/integrated_signaler/signal/receive_signal(var/datum/signal/signal)
+ if(!signal)
+ return FALSE
+ if(signal.source == src)
+ return FALSE
+ if(signal.data["message"] != "ACTIVATE")
+ return FALSE
+ if(isnull(signal.encryption))
+ return FALSE
+ if(within_jamming_range(src))
+ return FALSE
+
+ var/obj/item/computer_hardware/network_card/network_card = loc
+ if(istype(network_card))
+ return network_card.receive_signaler_signal(src, signal)
+ return FALSE
/obj/item/integrated_signaler/signal/Destroy()
- SSradio.remove_object(src, frequency)
+ if(SSradio)
+ SSradio.remove_object(src, frequency)
return ..()
diff --git a/code/game/objects/structures/machinery/atmoalter/canister.dm b/code/game/objects/structures/machinery/atmoalter/canister.dm
index 2a2093a19f3..71207747d2a 100644
--- a/code/game/objects/structures/machinery/atmoalter/canister.dm
+++ b/code/game/objects/structures/machinery/atmoalter/canister.dm
@@ -567,7 +567,23 @@ update_flag
/obj/structure/machinery/portable_atmospherics/canister/do_signaler()
valve_open = !valve_open
if(valve_open)
+ if(holding)
+ release_log += "Valve was opened by a signaler, starting the transfer into [holding]
"
+ else
+ release_log += "Valve was opened by a signaler, starting the transfer into the air
"
log_open_userless("a signaler")
+ if(!(processing_flags & MACHINERY_PROCESS_SELF))
+ START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF)
+ process()
+ else
+ if(holding)
+ release_log += "Valve was closed by a signaler, stopping the transfer into [holding]
"
+ else
+ release_log += "Valve was closed by a signaler, stopping the transfer into the air
"
+
+ update_connected_network()
+ update_icon()
+ SStgui.update_uis(src)
/**
* Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0
diff --git a/code/game/objects/structures/machinery/stasis_cage.dm b/code/game/objects/structures/machinery/stasis_cage.dm
index 49051c6a385..4f1a18347bb 100644
--- a/code/game/objects/structures/machinery/stasis_cage.dm
+++ b/code/game/objects/structures/machinery/stasis_cage.dm
@@ -252,65 +252,3 @@
if(target.buckled_to)
target.buckled_to.unbuckle()
contain(user, target)
-
-
-/datum/wires/stasis_cage
- proper_name = "Stasis Cage"
- holder_type = /obj/structure/machinery/stasis_cage
-
-
-/datum/wires/stasis_cage/New(atom/holder)
- wires = list(
- WIRE_SAFETY,
- WIRE_RELEASE,
- WIRE_LOCK
- )
- add_duds(3)
- ..()
-
-/datum/wires/stasis_cage/interactable(mob/user)
- SHOULD_CALL_PARENT(FALSE)
- var/obj/structure/machinery/stasis_cage/stasis_cage = holder
- if (stasis_cage.panel_open)
- return TRUE
- return FALSE
-
-
-/datum/wires/stasis_cage/get_status()
- var/obj/structure/machinery/stasis_cage/stasis_cage = holder
- . = ..()
- if(!stasis_cage.use_power)
- . += "The panel is unpowered."
- else
- . += "The panel is powered."
- . += "The biometric safety sensors are [(WIRE_SAFETY in cut_wires) ? "connected" : "disconnected"]."
- . += "The cage's emergency auto-release mechanism is [(WIRE_RELEASE in cut_wires) ? "disabled" : "enabled"]."
- . += "The cage lid motors are [(WIRE_LOCK in cut_wires) ? "overriden" : "nominal"]."
-
-
-/datum/wires/stasis_cage/on_cut(wire, mend, source)
- var/obj/structure/machinery/stasis_cage/stasis_cage = holder
- switch (wire)
- if (WIRE_SAFETY)
- stasis_cage.safety = !stasis_cage.safety
- if (WIRE_RELEASE)
- if (stasis_cage.contained && !mend)
- stasis_cage.release()
- holder.update_icon()
- if (WIRE_LOCK)
- if (!mend)
- playsound(stasis_cage.loc, 'sound/machines/BoltsDown.ogg', 60)
- holder.visible_message(SPAN_WARNING("The cage's lid bolts down destructively, denting itself!"), SPAN_NOTICE("You notice the cage lid override flag blink hastily."))
- stasis_cage.broken = TRUE
- holder.update_icon()
-
-
-/datum/wires/stasis_cage/on_pulse(wire)
- var/obj/structure/machinery/stasis_cage/stasis_cage = holder
- switch (wire)
- if (WIRE_SAFETY)
- if (stasis_cage.contained)
- if (prob(20))
- holder.visible_message(SPAN_WARNING("The cage hastily flicks open its lid!"), SPAN_NOTICE("You notice the biometric sensor flag blink fervently."))
- stasis_cage.release()
-
diff --git a/code/game/objects/structures/machinery/vending/engitech.dm b/code/game/objects/structures/machinery/vending/engitech.dm
index 2d5435f058a..899f3bb0123 100644
--- a/code/game/objects/structures/machinery/vending/engitech.dm
+++ b/code/game/objects/structures/machinery/vending/engitech.dm
@@ -22,7 +22,7 @@
/obj/item/flash/synthetic = 4,
/obj/item/cell/high = 12,
/obj/item/assembly/prox_sensor = 8,
- /obj/item/assembly/signaler = 8,
+ /obj/item/assembly/signaler = 20,
/obj/item/healthanalyzer = 3,
/obj/item/surgery/scalpel = 2,
/obj/item/surgery/circular_saw = 2,
@@ -62,6 +62,7 @@
/obj/item/multitool = 4,
/obj/item/taperoll/engineering = 4,
/obj/item/clothing/glasses/safety/goggles = 4,
+ /obj/item/assembly/signaler = 20,
/obj/item/airlock_electronics = 20,
/obj/item/module/power_control = 10,
/obj/item/airalarm_electronics = 10,
diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm
index 3e44f46c0dc..6537b9a1492 100644
--- a/code/modules/assembly/assembly.dm
+++ b/code/modules/assembly/assembly.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/assemblies/new_assemblies.dmi'
icon_state = ""
obj_flags = OBJ_FLAG_CONDUCTABLE
- w_class = WEIGHT_CLASS_SMALL
+ w_class = WEIGHT_CLASS_TINY
matter = list(MATERIAL_STEEL = 100)
recyclable = TRUE
throwforce = 2
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index ddfef2325e8..a01d976c17b 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -189,13 +189,22 @@
return FALSE
if(!secured)
audible_message("[icon2html(src, viewers(get_turf(src)))] *beep* *beep*", "*beep* *beep*")
+ return FALSE
if(normal && a_right && a_left)
- if(a_right != D)
- a_right.pulsed(FALSE)
- if(a_left != D)
- a_left.pulsed(FALSE)
+ 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)
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 85f4f0e2346..4b41b5f98e7 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -72,8 +72,9 @@
switch(action)
if("freq")
var/new_frequency = text2num(params["freq"]) * 10
- if(new_frequency < RADIO_LOW_FREQ || new_frequency > RADIO_HIGH_FREQ)
- new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
+ new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
+ if(new_frequency > RADIO_HIGH_FREQ)
+ new_frequency = RADIO_HIGH_FREQ - 1
set_frequency(new_frequency)
if("code")
diff --git a/code/modules/modular_computers/computers/subtypes/preset_handheld.dm b/code/modules/modular_computers/computers/subtypes/preset_handheld.dm
index 38e95f30650..dfe6cf16df0 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_handheld.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_handheld.dm
@@ -60,6 +60,10 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/silver
+/obj/item/modular_computer/handheld/preset/engineering/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/preset/engineering/atmos
_app_preset_type = /datum/modular_computer_app_presets/engineering/atmos
@@ -86,6 +90,10 @@
/obj/item/modular_computer/handheld/preset/supply/machinist
_app_preset_type = /datum/modular_computer_app_presets/supply/machinist
+/obj/item/modular_computer/handheld/preset/supply/machinist/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
// Cargo Delivery
/obj/item/modular_computer/handheld/preset/supply/cargo_delivery
_app_preset_type = /datum/modular_computer_app_presets/cargo_delivery
@@ -125,6 +133,10 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/white
+/obj/item/modular_computer/handheld/preset/research/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/preset/research/rd
_app_preset_type = /datum/modular_computer_app_presets/research/rd
diff --git a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm
index 3e0bef3bb46..c86a6cf2c0f 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm
@@ -31,6 +31,11 @@
_app_preset_type = /datum/modular_computer_app_presets/engineering
enrolled = DEVICE_COMPANY
+/obj/item/modular_computer/laptop/preset/engineering/install_default_hardware()
+ ..()
+ QDEL_NULL(network_card)
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/laptop/preset/engineering/ce
name = "chief engineer's laptop"
desc = "A portable computer belonging to the chief engineer."
@@ -58,6 +63,8 @@
/obj/item/modular_computer/laptop/preset/research/install_default_hardware()
..()
ai_slot = new /obj/item/computer_hardware/ai_slot(src)
+ QDEL_NULL(network_card)
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
/obj/item/modular_computer/laptop/preset/research/rd
name = "research director's laptop"
@@ -130,6 +137,8 @@
/obj/item/modular_computer/laptop/preset/supply/robotics/install_default_hardware()
..()
access_cable_dongle = new /obj/item/computer_hardware/access_cable_dongle(src)
+ QDEL_NULL(network_card)
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
// Representative
/obj/item/modular_computer/laptop/preset/representative
diff --git a/code/modules/modular_computers/computers/subtypes/preset_pda.dm b/code/modules/modular_computers/computers/subtypes/preset_pda.dm
index 667add4c92a..70373c8452d 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_pda.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_pda.dm
@@ -60,6 +60,10 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/silver
+/obj/item/modular_computer/handheld/pda/engineering/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/pda/engineering/atmos
_app_preset_type = /datum/modular_computer_app_presets/engineering/atmos
@@ -82,6 +86,10 @@
/obj/item/modular_computer/handheld/pda/supply/machinist
_app_preset_type = /datum/modular_computer_app_presets/supply/machinist
+/obj/item/modular_computer/handheld/pda/supply/machinist/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/pda/supply/om
icon_add = "q"
@@ -117,6 +125,10 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/white
+/obj/item/modular_computer/handheld/pda/research/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/pda/research/rd
_app_preset_type = /datum/modular_computer_app_presets/research/rd
icon_add = "rd"
diff --git a/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm b/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm
index 201c56d25df..f359db41de0 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm
@@ -154,6 +154,10 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/silver
+/obj/item/modular_computer/handheld/wristbound/preset/pda/engineering/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/wristbound/preset/pda/engineering/atmos
_app_preset_type = /datum/modular_computer_app_presets/engineering/atmos
@@ -183,6 +187,10 @@
/obj/item/modular_computer/handheld/wristbound/preset/pda/supply/machinist
_app_preset_type = /datum/modular_computer_app_presets/supply/machinist
+/obj/item/modular_computer/handheld/wristbound/preset/pda/supply/machinist/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
// Medical
/obj/item/modular_computer/handheld/wristbound/preset/pda/medical
@@ -211,6 +219,10 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/white
+/obj/item/modular_computer/handheld/wristbound/preset/pda/research/install_default_hardware()
+ ..()
+ network_card = new /obj/item/computer_hardware/network_card/signaler(src)
+
/obj/item/modular_computer/handheld/wristbound/preset/pda/research/rd
_app_preset_type = /datum/modular_computer_app_presets/research/rd
icon_add = "rd"
diff --git a/code/modules/modular_computers/file_system/programs/app_presets_.dm b/code/modules/modular_computers/file_system/programs/app_presets_.dm
index bc158b9cb32..ed3c1d6c222 100644
--- a/code/modules/modular_computers/file_system/programs/app_presets_.dm
+++ b/code/modules/modular_computers/file_system/programs/app_presets_.dm
@@ -55,6 +55,7 @@ ABSTRACT_TYPE(/datum/modular_computer_app_presets)
/datum/computer_file/program/camera_monitor,\
/datum/computer_file/program/lighting_control,\
/datum/computer_file/program/fusion_codex,\
+ /datum/computer_file/program/signaler,\
)
#define COMPUTER_APP_PRESET_HORIZON_MEDICAL list(\
@@ -71,6 +72,7 @@ ABSTRACT_TYPE(/datum/modular_computer_app_presets)
/datum/computer_file/program/scanner/science,\
/datum/computer_file/program/scanner/gas,\
/datum/computer_file/program/away_manifest,\
+ /datum/computer_file/program/signaler,\
)
#define COMPUTER_APP_PRESET_HORIZON_SECURITY list(\
diff --git a/code/modules/modular_computers/file_system/programs/app_presets_crew.dm b/code/modules/modular_computers/file_system/programs/app_presets_crew.dm
index 1243f2f0a4d..c1c089ead41 100644
--- a/code/modules/modular_computers/file_system/programs/app_presets_crew.dm
+++ b/code/modules/modular_computers/file_system/programs/app_presets_crew.dm
@@ -294,7 +294,7 @@
/datum/modular_computer_app_presets/supply/machinist/New()
. = ..()
- program_list += list(/datum/computer_file/program/records/medical, /datum/computer_file/program/scanner/science, /datum/computer_file/program/robotics)
+ program_list += list(/datum/computer_file/program/records/medical, /datum/computer_file/program/scanner/science, /datum/computer_file/program/robotics, /datum/computer_file/program/signaler)
//Machinist is the bastard child of supply/operation, it doesn't have access to shit essentially
program_list -= list(/datum/computer_file/program/civilian/cargocontrol, /datum/computer_file/program/civilian/cargodelivery, /datum/computer_file/program/away_manifest)
diff --git a/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm b/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm
index f987b424c0c..505814de9c9 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/fusion_codex.dm
@@ -4,7 +4,7 @@
program_icon_state = "medcomp"
program_key_icon_state = "teal_key"
extended_desc = "Useful program to reference reaction chains for nuclear fusion reactors."
- size = 14
+ size = 4
requires_access_to_run = PROGRAM_ACCESS_LIST_ONE
required_access_run = list(ACCESS_ENGINE)
required_access_download = list(ACCESS_ENGINE)
diff --git a/code/modules/modular_computers/file_system/programs/generic/signaler.dm b/code/modules/modular_computers/file_system/programs/generic/signaler.dm
new file mode 100644
index 00000000000..322c332ed44
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/generic/signaler.dm
@@ -0,0 +1,249 @@
+#define NTOS_SIGNALER_CHANNEL_COUNT 8
+#define NTOS_SIGNALER_RECEIVE_CHANNELS 2
+#define NTOS_SIGNALER_DEFAULT_FREQUENCY 1457
+#define NTOS_SIGNALER_DEFAULT_CODE 30
+#define NTOS_SIGNALER_MAX_LABEL_LENGTH 24
+
+/datum/computer_file/program/signaler
+ filename = "signaler"
+ filedesc = "Signal Manager"
+ extended_desc = "This program configures signaler-capable network cards for receiving and sending signals on custom frequencies."
+ program_icon_state = "generic"
+ program_key_icon_state = "green_key"
+ color = LIGHT_COLOR_GREEN
+ size = 1
+ requires_ntnet = FALSE
+ available_on_ntnet = TRUE
+ usage_flags = PROGRAM_ALL_REGULAR
+ tgui_id = "NTOSSignaler"
+
+ var/list/signal_configurations = list()
+
+/datum/computer_file/program/signaler/New(obj/item/modular_computer/comp)
+ . = ..()
+ ensure_signal_configurations()
+
+/datum/computer_file/program/signaler/clone(var/rename = FALSE, var/computer)
+ ensure_signal_configurations()
+ var/datum/computer_file/program/signaler/signaler_clone = ..(rename, computer)
+ signaler_clone.signal_configurations = list()
+ for(var/list/configuration in signal_configurations)
+ signaler_clone.signal_configurations += list(configuration.Copy())
+ return signaler_clone
+
+/datum/computer_file/program/signaler/run_program(var/mob/user)
+ if(!..())
+ return FALSE
+ sync_receive_radios()
+ return TRUE
+
+/datum/computer_file/program/signaler/ui_data(mob/user)
+ ensure_signal_configurations()
+ sync_receive_radios()
+
+ var/list/data = initial_data()
+ data["hardware_ready"] = !!get_signaler_card()
+ data["hardware_status"] = get_hardware_status()
+ data["min_frequency"] = RADIO_LOW_FREQ
+ data["max_frequency"] = RADIO_HIGH_FREQ
+ data["receive_channels"] = NTOS_SIGNALER_RECEIVE_CHANNELS
+ data["silent"] = silent
+ data["channels"] = list()
+
+ for(var/i = 1 to NTOS_SIGNALER_CHANNEL_COUNT)
+ var/list/configuration = signal_configurations[i]
+ data["channels"] += list(list(
+ "index" = i,
+ "label" = configuration["label"],
+ "frequency" = configuration["frequency"],
+ "code" = configuration["code"],
+ "receiving" = i <= NTOS_SIGNALER_RECEIVE_CHANNELS,
+ "receiving_enabled" = configuration["receiving_enabled"],
+ "last_received_code" = configuration["last_received_code"],
+ "last_received_time" = configuration["last_received_time"]
+ ))
+
+ return data
+
+/datum/computer_file/program/signaler/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ if(..())
+ return TRUE
+
+ if(action == "toggle_silent")
+ silent = !silent
+ . = TRUE
+ return
+
+ ensure_signal_configurations()
+ var/channel_index = clamp(round(text2num(params["index"])), 1, NTOS_SIGNALER_CHANNEL_COUNT)
+ var/list/configuration = signal_configurations[channel_index]
+ if(!configuration)
+ return TRUE
+
+ switch(action)
+ if("rename")
+ var/new_label = tgui_input_text(usr, "Enter a new label for this [channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS ? "receiver" : "signaler"].", "Rename Signal", configuration["label"], NTOS_SIGNALER_MAX_LABEL_LENGTH)
+ if(isnull(new_label))
+ return TRUE
+ new_label = sanitize(new_label, NTOS_SIGNALER_MAX_LABEL_LENGTH)
+ configuration["label"] = length(new_label) ? new_label : default_label(channel_index)
+ . = TRUE
+ if("frequency")
+ configuration["frequency"] = clean_frequency(text2num(params["frequency"]))
+ if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS)
+ sync_receive_radios()
+ . = TRUE
+ if("toggle_receive")
+ if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS)
+ configuration["receiving_enabled"] = !configuration["receiving_enabled"]
+ if(!configuration["receiving_enabled"])
+ configuration["last_received_code"] = null
+ configuration["last_received_time"] = null
+ sync_receive_radios()
+ . = TRUE
+ if("code")
+ if(channel_index > NTOS_SIGNALER_RECEIVE_CHANNELS)
+ configuration["code"] = clamp(round(text2num(params["code"])), 1, 100)
+ . = TRUE
+ if("reset")
+ configuration["frequency"] = NTOS_SIGNALER_DEFAULT_FREQUENCY
+ configuration["code"] = NTOS_SIGNALER_DEFAULT_CODE
+ if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS)
+ configuration["receiving_enabled"] = FALSE
+ configuration["last_received_code"] = null
+ configuration["last_received_time"] = null
+ sync_receive_radios()
+ . = TRUE
+ if("send")
+ if(channel_index > NTOS_SIGNALER_RECEIVE_CHANNELS)
+ send_signal(channel_index, usr)
+ . = TRUE
+
+/datum/computer_file/program/signaler/proc/ensure_signal_configurations()
+ for(var/i = 1 to min(length(signal_configurations), NTOS_SIGNALER_CHANNEL_COUNT))
+ var/list/configuration = signal_configurations[i]
+ if(!configuration)
+ continue
+ if(isnull(configuration["receiving_enabled"]))
+ configuration["receiving_enabled"] = FALSE
+
+ for(var/i = length(signal_configurations) + 1 to NTOS_SIGNALER_CHANNEL_COUNT)
+ signal_configurations += list(list(
+ "label" = default_label(i),
+ "frequency" = NTOS_SIGNALER_DEFAULT_FREQUENCY,
+ "code" = NTOS_SIGNALER_DEFAULT_CODE,
+ "receiving_enabled" = FALSE,
+ "last_received_code" = null,
+ "last_received_time" = null
+ ))
+
+/datum/computer_file/program/signaler/proc/default_label(var/channel_index)
+ if(channel_index <= NTOS_SIGNALER_RECEIVE_CHANNELS)
+ return "Receiver [channel_index]"
+ return "Signal [channel_index - NTOS_SIGNALER_RECEIVE_CHANNELS]"
+
+/datum/computer_file/program/signaler/proc/clean_frequency(var/frequency)
+ if(!isnum(frequency))
+ frequency = NTOS_SIGNALER_DEFAULT_FREQUENCY
+ frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
+ if(frequency > RADIO_HIGH_FREQ)
+ frequency = RADIO_HIGH_FREQ - 1
+ return frequency
+
+/datum/computer_file/program/signaler/proc/get_signaler_card()
+ if(!computer?.network_card)
+ return null
+ var/obj/item/computer_hardware/network_card/network_card = computer.network_card
+ if(!network_card.enabled || !network_card.check_functionality() || !network_card.sradio)
+ return null
+ return network_card
+
+/datum/computer_file/program/signaler/proc/get_hardware_status()
+ if(!computer?.network_card)
+ return "No network card detected."
+ var/obj/item/computer_hardware/network_card/network_card = computer.network_card
+ if(!network_card.sradio)
+ return "Installed network card does not support signaling."
+ if(!network_card.enabled)
+ return "Signaler network card is disabled."
+ if(!network_card.check_functionality())
+ return "Signaler network card is damaged."
+ return "Ready."
+
+/datum/computer_file/program/signaler/proc/sync_receive_radios()
+ var/obj/item/computer_hardware/network_card/network_card = get_signaler_card()
+ if(!network_card)
+ return FALSE
+
+ for(var/i = 1 to NTOS_SIGNALER_RECEIVE_CHANNELS)
+ var/obj/item/integrated_signaler/signal/radio = network_card.get_signaler_radio(i)
+ var/list/configuration = signal_configurations[i]
+ if(radio && configuration)
+ if(configuration["receiving_enabled"])
+ radio.set_frequency(configuration["frequency"])
+ radio.set_listening(TRUE)
+ else
+ radio.set_listening(FALSE)
+
+ return TRUE
+
+/datum/computer_file/program/signaler/proc/send_signal(var/channel_index, var/mob/user)
+ var/obj/item/computer_hardware/network_card/network_card = get_signaler_card()
+ if(!network_card)
+ computer?.output_error(FONT_SMALL(SPAN_WARNING("Hardware Error - Signaler network card unavailable.")))
+ return FALSE
+
+ var/list/configuration = signal_configurations[channel_index]
+ if(!configuration)
+ return FALSE
+
+ var/obj/item/integrated_signaler/signal/radio = network_card.get_signaler_radio(0)
+ if(!radio)
+ return FALSE
+
+ var/old_frequency = radio.frequency
+ var/old_code = radio.code
+
+ radio.set_frequency(configuration["frequency"])
+ radio.code = configuration["code"]
+ var/sent = radio.send_signal("ACTIVATE", user)
+ radio.code = old_code
+ radio.set_frequency(old_frequency)
+
+ if(sent)
+ computer?.output_notice("Signal sent on [format_frequency(configuration["frequency"])]/[configuration["code"]].", 0)
+ else
+ computer?.output_error(FONT_SMALL(SPAN_WARNING("Unable to send signal. The transmitter may be cooling down or jammed.")))
+
+ return sent
+
+/datum/computer_file/program/signaler/proc/receive_signal(var/channel_index, var/datum/signal/signal)
+ if(channel_index < 1 || channel_index > NTOS_SIGNALER_RECEIVE_CHANNELS)
+ return FALSE
+
+ ensure_signal_configurations()
+ var/list/configuration = signal_configurations[channel_index]
+ if(!configuration)
+ return FALSE
+ if(!configuration["receiving_enabled"])
+ return FALSE
+
+ configuration["last_received_code"] = signal.encryption
+ configuration["last_received_time"] = worldtime2text()
+
+ if(computer)
+ var/received_message = "Signal received on [configuration["label"]]: [isnull(signal.encryption) ? "no code" : signal.encryption]."
+ if(silent)
+ computer.output_notice(received_message, 0)
+ else
+ playsound(get_turf(computer), 'sound/machines/twobeep.ogg', 20, 1)
+ computer.audible_message("[icon2html(computer, viewers(1, get_turf(computer)))] [computer] beeps, \"[received_message]\"")
+
+ SStgui.update_uis(src)
+ return TRUE
+
+#undef NTOS_SIGNALER_CHANNEL_COUNT
+#undef NTOS_SIGNALER_RECEIVE_CHANNELS
+#undef NTOS_SIGNALER_DEFAULT_FREQUENCY
+#undef NTOS_SIGNALER_DEFAULT_CODE
+#undef NTOS_SIGNALER_MAX_LABEL_LENGTH
diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm
index 182fa048843..cad9523c77b 100644
--- a/code/modules/modular_computers/hardware/network_card.dm
+++ b/code/modules/modular_computers/hardware/network_card.dm
@@ -13,6 +13,8 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1)
var/long_range = FALSE
var/ethernet = FALSE // Hard-wired, therefore always on, ignores NTNet wireless checks.
var/obj/item/integrated_signaler/signal/sradio = FALSE // integrated signaler - not present on basic model.
+ var/obj/item/integrated_signaler/signal/secondary_sradio = FALSE // second receiver for NTOS signaler software.
+ var/obj/item/integrated_signaler/signal/transmit_sradio = FALSE // transmitter for NTOS signaler software.
malfunction_probability = 1
/obj/item/computer_hardware/network_card/diagnostics(mob/user)
@@ -41,7 +43,7 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1)
/obj/item/computer_hardware/network_card/signaler/Initialize()
. = ..()
- sradio = new /obj/item/integrated_signaler/signal(src)
+ initialize_signaler_radios()
/obj/item/computer_hardware/network_card/advanced
name = "advanced NTNet network card"
@@ -54,7 +56,7 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1)
/obj/item/computer_hardware/network_card/advanced/Initialize()
. = ..()
- sradio = new /obj/item/integrated_signaler/signal(src)
+ initialize_signaler_radios()
/obj/item/computer_hardware/network_card/wired
name = "wired NTNet network card"
@@ -67,7 +69,46 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1)
/obj/item/computer_hardware/network_card/wired/Initialize()
. = ..()
- sradio = new /obj/item/integrated_signaler/signal(src)
+ initialize_signaler_radios()
+
+/obj/item/computer_hardware/network_card/proc/initialize_signaler_radios()
+ if(!sradio)
+ sradio = new /obj/item/integrated_signaler/signal/network_card(src)
+ sradio.set_listening(FALSE)
+ if(!secondary_sradio)
+ secondary_sradio = new /obj/item/integrated_signaler/signal/network_card(src)
+ secondary_sradio.set_listening(FALSE)
+ if(!transmit_sradio)
+ transmit_sradio = new /obj/item/integrated_signaler/signal/network_card(src)
+ transmit_sradio.set_listening(FALSE)
+
+/obj/item/computer_hardware/network_card/proc/get_signaler_radio(var/channel = 1)
+ if(channel == 0)
+ return transmit_sradio
+ if(channel == 2)
+ return secondary_sradio
+ return sradio
+
+/obj/item/computer_hardware/network_card/proc/receive_signaler_signal(var/obj/item/integrated_signaler/signal/receiver, var/datum/signal/signal)
+ if(!parent_computer || !parent_computer.enabled || !enabled || !check_functionality())
+ return FALSE
+
+ var/channel
+ if(receiver == sradio)
+ channel = 1
+ else if(receiver == secondary_sradio)
+ channel = 2
+ else
+ return FALSE
+
+ if(istype(parent_computer.active_program, /datum/computer_file/program/signaler))
+ var/datum/computer_file/program/signaler/active_signaler = parent_computer.active_program
+ active_signaler.receive_signal(channel, signal)
+
+ for(var/datum/computer_file/program/signaler/signaler_program in parent_computer.idle_threads)
+ signaler_program.receive_signal(channel, signal)
+
+ return TRUE
// Returns a string identifier of this network card
/obj/item/computer_hardware/network_card/proc/get_network_tag()
@@ -106,6 +147,10 @@ GLOBAL_VAR_INIT(ntnet_card_uid, 1)
/obj/item/computer_hardware/network_card/Destroy()
if(sradio)
QDEL_NULL(sradio)
+ if(secondary_sradio)
+ QDEL_NULL(secondary_sradio)
+ if(transmit_sradio)
+ QDEL_NULL(transmit_sradio)
if(parent_computer?.network_card == src)
parent_computer.network_card = null
parent_computer = null
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 1b1bd57fc3b..fd774f97be5 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -175,6 +175,8 @@ ABSTRACT_TYPE(/obj/structure/machinery/power/apc)
/// If we're actually able to charge
var/charge_mode = CHARGE_MODE_CHARGE
var/last_time = 1
+ /// For doing silly games with blowing out lights
+ var/light_explosion_safety = TRUE
/obj/structure/machinery/power/apc/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 20beaa5d926..81be560f2f4 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -159,6 +159,7 @@
SPAN_NOTICE("You detach [rig] from \the [src]"))
rig.forceMove(get_turf(user))
+ rig.detached()
rig = null
overlays = new/list()
@@ -176,12 +177,15 @@
if (rig)
to_chat(user, SPAN_WARNING("There is another device in the way."))
return ..()
+ var/obj/item/assembly_holder/H = attacking_item
+ if(!H.secured)
+ to_chat(user, SPAN_WARNING("The assembly must be secured with a screwdriver."))
+ return TRUE
user.visible_message("[user] begins rigging [attacking_item] to \the [src].", "You begin rigging [attacking_item] to \the [src]")
if(do_after(user, 20))
user.visible_message(SPAN_NOTICE("[user] rigs [attacking_item] to \the [src]."),
SPAN_NOTICE("You rig [attacking_item] to \the [src]"))
- var/obj/item/assembly_holder/H = attacking_item
if (istype(H.a_left,/obj/item/assembly/igniter) || istype(H.a_right,/obj/item/assembly/igniter))
message_admins("[key_name_admin(user)] rigged fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) for explosion. (JMP)")
log_game("[key_name(user)] rigged fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) for explosion.")
diff --git a/html/changelogs/Bat-SignalsWires.yml b/html/changelogs/Bat-SignalsWires.yml
new file mode 100644
index 00000000000..fac848d5aff
--- /dev/null
+++ b/html/changelogs/Bat-SignalsWires.yml
@@ -0,0 +1,13 @@
+author: Batrachophrenoboocosmomachia
+delete-after: True
+changes:
+ - qol: "Eligible non-random wire panels now use relevant domain skills where applicable: engineering skills, robotics, and xenobiology implemented, instead of only Electrical Engineering 4."
+ - rscadd: "Adds new wire to APCs that blows out all the lights if you pulse it with the safeties off."
+ - rscadd: "Adds the Signal Manager NTOS app for signaler-capable network cards. Pre-installed in Engineering, Research, and Machinist app presets."
+ - rscadd: "Engineering, Research, and Machinist character modular computers now come with signaler-capable network cards."
+ - qol: "Increases remote signaler availability in EngiTech vendors and made assembly items tiny."
+ - bugfix: "Fixes air alarm wiring to use the correct wire datum path."
+ - bugfix: "Fixes canister-attached signaler regression."
+ - bugfix: "Fixes assembly holders failing silently when unsecured to fuel tanks; fuel tanks now reject unsecured assembly holders."
+ - bugfix: "Fixes runtime when an activation deletes or detaches one of the paired assemblies."
+ - qol: "Reduces the Fusion Codex program size."
diff --git a/tgui/packages/tgui/interfaces/NTOSSignaler.tsx b/tgui/packages/tgui/interfaces/NTOSSignaler.tsx
new file mode 100644
index 00000000000..b5ffdddb985
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NTOSSignaler.tsx
@@ -0,0 +1,187 @@
+import {
+ Box,
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+ Table,
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
+
+import { useBackend } from '../backend';
+import { NtosWindow } from '../layouts';
+
+type SignalerChannel = {
+ index: number;
+ label: string;
+ frequency: number;
+ code: number;
+ receiving: boolean;
+ receiving_enabled: boolean;
+ last_received_code: number | null;
+ last_received_time: string | null;
+};
+
+type Data = {
+ hardware_ready: boolean;
+ hardware_status: string;
+ min_frequency: number;
+ max_frequency: number;
+ receive_channels: number;
+ silent: boolean;
+ channels: SignalerChannel[];
+};
+
+export const NTOSSignaler = () => {
+ const { act, data } = useBackend();
+ const { channels = [] } = data;
+ const rows: SignalerChannel[][] = [];
+
+ for (let i = 0; i < channels.length; i += 2) {
+ rows.push(channels.slice(i, i + 2));
+ }
+
+ return (
+
+
+ act('toggle_silent')}
+ />
+ }
+ >
+
+ {data.hardware_status}
+
+
+
+ {rows.map((row) => (
+
+ {row.map((channel) => (
+
+
+
+ ))}
+
+ ))}
+
+
+
+ );
+};
+
+const SignalSlot = (props: { channel: SignalerChannel; data: Data }) => {
+ const { act } = useBackend();
+ const { channel, data } = props;
+ const channelNumber = channel.receiving
+ ? channel.index
+ : channel.index - data.receive_channels;
+
+ return (
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/Signaler.tsx b/tgui/packages/tgui/interfaces/Signaler.tsx
index 5882bc3d29c..db31351e4f8 100644
--- a/tgui/packages/tgui/interfaces/Signaler.tsx
+++ b/tgui/packages/tgui/interfaces/Signaler.tsx
@@ -32,15 +32,15 @@ export const Signaler = (props) => {
toFixed(value, 2)}
+ format={(value) => toFixed(value, 1)}
width="80px"
- onChange={(value) => act('freq', { freq: value * 10 })}
+ onChange={(value) => act('freq', { freq: value })}
/>