From c92d9095342425defc25c0cc0fca2feb7f17c641 Mon Sep 17 00:00:00 2001 From: Batrachophreno Date: Sun, 26 Jul 2026 14:05:15 -0400 Subject: [PATCH] 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: Screenshot 2026-07-14 144049 Self-explanatory. Please only use corporate-provided software for behavior within your contract regulations. --- aurorastation.dme | 4 +- code/__DEFINES/wires.dm | 3 - .../datums/skills/occupational/engineering.dm | 34 ++- code/datums/skills/occupational/operations.dm | 5 +- code/datums/skills/occupational/science.dm | 3 + code/datums/wires/airlock.dm | 3 +- code/datums/wires/alarm.dm | 7 +- code/datums/wires/apc.dm | 21 +- code/datums/wires/disposals.dm | 2 +- code/datums/wires/explosive.dm | 4 + code/datums/wires/fabricator.dm | 1 + .../wires/hardsuit.dm} | 2 +- code/datums/wires/ipc_tag_scanner.dm | 2 +- code/datums/wires/landmine.dm | 4 + code/datums/wires/nuclearbomb.dm | 2 + code/datums/wires/particle_accelerator.dm | 1 + code/datums/wires/robot.dm | 2 +- code/datums/wires/stasis_cage.dm | 56 ++++ code/datums/wires/suit_storage_unit.dm | 1 + code/datums/wires/vending.dm | 3 +- code/datums/wires/wires.dm | 75 +++++- code/game/objects/items/devices/PDA/radio.dm | 77 +++++- .../machinery/atmoalter/canister.dm | 16 ++ .../structures/machinery/stasis_cage.dm | 62 ----- .../structures/machinery/vending/engitech.dm | 3 +- code/modules/assembly/assembly.dm | 2 +- code/modules/assembly/holder.dm | 17 +- code/modules/assembly/signaler.dm | 5 +- .../computers/subtypes/preset_handheld.dm | 12 + .../computers/subtypes/preset_laptop.dm | 9 + .../computers/subtypes/preset_pda.dm | 12 + .../computers/subtypes/preset_wristbound.dm | 12 + .../file_system/programs/app_presets_.dm | 2 + .../file_system/programs/app_presets_crew.dm | 2 +- .../programs/engineering/fusion_codex.dm | 2 +- .../file_system/programs/generic/signaler.dm | 249 ++++++++++++++++++ .../hardware/network_card.dm | 51 +++- code/modules/power/apc.dm | 2 + code/modules/reagents/reagent_dispenser.dm | 6 +- html/changelogs/Bat-SignalsWires.yml | 13 + .../packages/tgui/interfaces/NTOSSignaler.tsx | 187 +++++++++++++ tgui/packages/tgui/interfaces/Signaler.tsx | 12 +- 42 files changed, 858 insertions(+), 130 deletions(-) rename code/{modules/clothing/spacesuits/rig/rig_wiring.dm => datums/wires/hardsuit.dm} (98%) create mode 100644 code/datums/wires/stasis_cage.dm create mode 100644 code/modules/modular_computers/file_system/programs/generic/signaler.dm create mode 100644 html/changelogs/Bat-SignalsWires.yml create mode 100644 tgui/packages/tgui/interfaces/NTOSSignaler.tsx 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 })} />