diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm
index 6ec17b1d52f..497e0c0b175 100644
--- a/code/__DEFINES/machines.dm
+++ b/code/__DEFINES/machines.dm
@@ -62,6 +62,7 @@
#define MC_CHARGE "CHARGE"
#define MC_AI "AI"
#define MC_SENSORS "SENSORS"
+#define MC_SIGNALER "SIGNALER"
//NTNet stuff, for modular computers
// NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc)
diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm
index f140ae63d15..7ddda4ab706 100644
--- a/code/modules/jobs/job_types/roboticist.dm
+++ b/code/modules/jobs/job_types/roboticist.dm
@@ -44,6 +44,7 @@
backpack = /obj/item/storage/backpack/science
satchel = /obj/item/storage/backpack/satchel/tox
duffelbag = /obj/item/storage/backpack/duffelbag/toxins
+ backpack_contents = list(/obj/item/modular_computer/tablet/preset/science=1)
pda_slot = ITEM_SLOT_LPOCKET
diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm
index 0bf0141092a..9760686722f 100644
--- a/code/modules/jobs/job_types/scientist.dm
+++ b/code/modules/jobs/job_types/scientist.dm
@@ -40,6 +40,7 @@
backpack = /obj/item/storage/backpack/science
satchel = /obj/item/storage/backpack/satchel/tox
duffelbag = /obj/item/storage/backpack/duffelbag/toxins
+ backpack_contents = list(/obj/item/modular_computer/tablet/preset/science=1)
id_trim = /datum/id_trim/job/scientist
diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm
index db2f4d51b3b..3e246cf24de 100644
--- a/code/modules/modular_computers/computers/item/tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/tablet_presets.dm
@@ -20,6 +20,17 @@
install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/printer/mini)
+/obj/item/modular_computer/tablet/preset/science/Initialize()
+ . = ..()
+ var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
+ install_component(new /obj/item/computer_hardware/processor_unit/small)
+ install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
+ install_component(hard_drive)
+ install_component(new /obj/item/computer_hardware/card_slot)
+ install_component(new /obj/item/computer_hardware/network_card)
+ install_component(new /obj/item/computer_hardware/radio_card)
+ hard_drive.store_file(new /datum/computer_file/program/signaler)
+
/obj/item/modular_computer/tablet/preset/cargo/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
diff --git a/code/modules/modular_computers/file_system/programs/signaler.dm b/code/modules/modular_computers/file_system/programs/signaler.dm
new file mode 100644
index 00000000000..abde8811bd5
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/signaler.dm
@@ -0,0 +1,83 @@
+/datum/computer_file/program/signaler
+ filename = "signaler"
+ filedesc = "SignalCommander"
+ category = PROGRAM_CATEGORY_MISC
+ program_icon_state = "signal"
+ extended_desc = "A small built-in frequency app that sends out signaller signals with the appropriate hardware."
+ size = 2
+ tgui_id = "NtosSignaler"
+ program_icon = "satellite-dish"
+ usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP
+ ///What is the saved signal frequency?
+ var/signal_frequency = FREQ_SIGNALER
+ /// What is the saved signal code?
+ var/signal_code = DEFAULT_SIGNALER_CODE
+ /// Radio connection datum used by signalers.
+ var/datum/radio_frequency/radio_connection
+
+/datum/computer_file/program/signaler/run_program(mob/living/user)
+ . = ..()
+ if (!.)
+ return
+ if(!computer?.get_modular_computer_part(MC_SIGNALER)) //Giving a clue to users why the program is spitting out zeros.
+ to_chat(user, "\The [computer] flashes an error: \"hardware\\signal_hardware\\startup.bin -- file not found\".")
+
+
+/datum/computer_file/program/signaler/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/obj/item/computer_hardware/radio_card/sensor = computer?.get_modular_computer_part(MC_SIGNALER)
+ if(sensor?.check_functionality())
+ data["frequency"] = signal_frequency
+ data["code"] = signal_code
+ data["minFrequency"] = MIN_FREE_FREQ
+ data["maxFrequency"] = MAX_FREE_FREQ
+ return data
+
+/datum/computer_file/program/signaler/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+ var/obj/item/computer_hardware/radio_card/sensor = computer?.get_modular_computer_part(MC_SIGNALER)
+ if(!(sensor?.check_functionality()))
+ playsound(src, 'sound/machines/scanbuzz.ogg', 100, FALSE)
+ return
+ switch(action)
+ if("signal")
+ INVOKE_ASYNC(src, .proc/signal)
+ . = TRUE
+ if("freq")
+ signal_frequency = unformat_frequency(params["freq"])
+ signal_frequency = sanitize_frequency(signal_frequency, TRUE)
+ set_frequency(signal_frequency)
+ . = TRUE
+ if("code")
+ signal_code = text2num(params["code"])
+ signal_code = round(signal_code)
+ . = TRUE
+ if("reset")
+ if(params["reset"] == "freq")
+ signal_frequency = initial(signal_frequency)
+ else
+ signal_code = initial(signal_code)
+ . = TRUE
+
+/datum/computer_file/program/signaler/proc/signal()
+ if(!radio_connection)
+ return
+
+ var/time = time2text(world.realtime,"hh:mm:ss")
+ var/turf/T = get_turf(src)
+
+ var/logging_data
+ if(usr)
+ logging_data = "[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(signal_frequency)]/[signal_code]"
+ GLOB.lastsignalers.Add(logging_data)
+
+ var/datum/signal/signal = new(list("code" = signal_code), logging_data = logging_data)
+ radio_connection.post_signal(src, signal)
+
+/datum/computer_file/program/signaler/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, signal_frequency)
+ signal_frequency = new_frequency
+ radio_connection = SSradio.add_object(src, signal_frequency, RADIO_SIGNALER)
+ return
diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm
index a82f18c8ca6..ad314b37559 100644
--- a/code/modules/modular_computers/hardware/_hardware.dm
+++ b/code/modules/modular_computers/hardware/_hardware.dm
@@ -9,17 +9,29 @@
var/obj/item/modular_computer/holder = null
// Computer that holds this hardware, if any.
- var/power_usage = 0 // If the hardware uses extra power, change this.
- var/enabled = TRUE // If the hardware is turned off set this to 0.
- var/critical = FALSE // Prevent disabling for important component, like the CPU.
- var/can_install = TRUE // Prevents direct installation of removable media.
- var/expansion_hw = FALSE // Hardware that fits into expansion bays.
- var/removable = TRUE // Whether the hardware is removable or not.
- var/damage = 0 // Current damage level
- var/max_damage = 100 // Maximal damage level.
- var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
- var/damage_failure = 50 // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all.
- var/malfunction_probability = 10// Chance of malfunction when the component is damaged
+ // If the hardware uses extra power, change this.
+ var/power_usage = 0
+ // If the hardware is turned off set this to 0.
+ var/enabled = TRUE
+ // Prevent disabling for important component, like the CPU.
+ var/critical = FALSE
+ // Prevents direct installation of removable media.
+ var/can_install = TRUE
+ // Hardware that fits into expansion bays.
+ var/expansion_hw = FALSE
+ // Whether the hardware is removable or not.
+ var/removable = TRUE
+ // Current damage level
+ var/damage = 0
+// Maximal damage level.
+ var/max_damage = 100
+ // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
+ var/damage_malfunction = 20
+ // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all.
+ var/damage_failure = 50
+ // Chance of malfunction when the component is damaged
+ var/malfunction_probability = 10
+ // What define is used to qualify this piece of hardware? Important for upgraded versions of the same hardware.
var/device_type
/obj/item/computer_hardware/New(obj/L)
diff --git a/code/modules/modular_computers/hardware/sensor_package.dm b/code/modules/modular_computers/hardware/sensor_package.dm
index c0363bc809b..0579b752fb0 100644
--- a/code/modules/modular_computers/hardware/sensor_package.dm
+++ b/code/modules/modular_computers/hardware/sensor_package.dm
@@ -6,3 +6,12 @@
w_class = WEIGHT_CLASS_TINY
device_type = MC_SENSORS
expansion_hw = TRUE
+
+/obj/item/computer_hardware/radio_card
+ name = "integrated radio card"
+ desc = "An integrated signaling assembly for computers to send an outgoing frequency signal. Required by certain programs."
+ icon_state = "signal_card"
+ w_class = WEIGHT_CLASS_TINY
+ device_type = MC_SIGNALER
+ expansion_hw = TRUE
+ power_usage = 10
diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm
index 5e338981615..e9cc46ee745 100644
--- a/code/modules/research/designs/computer_part_designs.dm
+++ b/code/modules/research/designs/computer_part_designs.dm
@@ -252,3 +252,12 @@
build_path = /obj/item/computer_hardware/sensorpackage
category = list("Computer Parts")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+
+/datum/design/signaler_part
+ name = "Integrated Signaler"
+ id = "signalpart"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/iron = 400, /datum/material/glass = 100)
+ build_path = /obj/item/computer_hardware/radio_card
+ category = list("Computer Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
diff --git a/code/modules/vending/modularpc.dm b/code/modules/vending/modularpc.dm
index 42c17dec249..d18f6839c0e 100644
--- a/code/modules/vending/modularpc.dm
+++ b/code/modules/vending/modularpc.dm
@@ -21,7 +21,8 @@
/obj/item/computer_hardware/ai_slot = 2,
/obj/item/computer_hardware/printer/mini = 2,
/obj/item/computer_hardware/recharger/apc_recharger = 2,
- /obj/item/paicard = 2)
+ /obj/item/paicard = 2,
+ /obj/item/computer_hardware/radio_card = 1)
refill_canister = /obj/item/vending_refill/modularpc
default_price = PAYCHECK_MEDIUM
extra_price = PAYCHECK_HARD
diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi
index 4c7f838db41..92536f9ca6c 100644
Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ
diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi
index 6b3dc82dda1..60d47a72402 100644
Binary files a/icons/obj/modular_tablet.dmi and b/icons/obj/modular_tablet.dmi differ
diff --git a/icons/obj/module.dmi b/icons/obj/module.dmi
index 813dbe83f71..707ab1b47e7 100644
Binary files a/icons/obj/module.dmi and b/icons/obj/module.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index d4ca1749896..a84c421c03b 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2825,6 +2825,7 @@
#include "code\modules\modular_computers\file_system\programs\robocontrol.dm"
#include "code\modules\modular_computers\file_system\programs\robotact.dm"
#include "code\modules\modular_computers\file_system\programs\secureye.dm"
+#include "code\modules\modular_computers\file_system\programs\signaler.dm"
#include "code\modules\modular_computers\file_system\programs\sm_monitor.dm"
#include "code\modules\modular_computers\file_system\programs\techweb.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\contract_uplink.dm"
diff --git a/tgui/packages/tgui/interfaces/NtosSignaler.js b/tgui/packages/tgui/interfaces/NtosSignaler.js
new file mode 100644
index 00000000000..5f948c85470
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosSignaler.js
@@ -0,0 +1,14 @@
+import { SignalerContent } from './Signaler';
+import { NtosWindow } from '../layouts';
+
+export const NtosSignaler = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/Signaler.js b/tgui/packages/tgui/interfaces/Signaler.js
index 36aa9c9f5d5..1eaf9868371 100644
--- a/tgui/packages/tgui/interfaces/Signaler.js
+++ b/tgui/packages/tgui/interfaces/Signaler.js
@@ -4,6 +4,19 @@ import { Button, Grid, NumberInput, Section } from '../components';
import { Window } from '../layouts';
export const Signaler = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+
+
+
+
+ );
+};
+
+export const SignalerContent = (props, context) => {
const { act, data } = useBackend(context);
const {
code,
@@ -11,81 +24,77 @@ export const Signaler = (props, context) => {
minFrequency,
maxFrequency,
} = data;
+ const color = 'rgba(13, 13, 213, 0.7)';
+ const backColor = 'rgba(0, 0, 69, 0.5)';
return (
-
-
-
-
-
- Frequency:
-
-
- toFixed(value, 1)}
- width="80px"
- onDrag={(e, value) => act('freq', {
- freq: value,
- })} />
-
-
-
-
-
-
- Code:
-
-
- act('code', {
- code: value,
- })} />
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Frequency:
+
+
+ toFixed(value, 1)}
+ width="80px"
+ onDrag={(e, value) => act('freq', {
+ freq: value,
+ })} />
+
+
+
+
+
+
+ Code:
+
+
+ act('code', {
+ code: value,
+ })} />
+
+
+
+
+
+
+
+
+
);
};