[MIRROR] Integrated circuits for modular computers (#26196)

Integrated circuits for modular computers (#80530)

This PR integrates circuits for modular computers and a good bits of
their programs.
The peculiarity here is that modular computers have no fixed amount of
unremovable components (except the base one with just a couple ports for
now), instead, they're added and removed along with programs. With a few
exceptions (such as the messenger and signaler), for these program
circuits to work, their associated program has to be either open or in
the background.

For a reason or another, not all programs have a circuit associated to
them, still, however the programs with a circuit are still a handful.
They are:
- Nanotrasen Pay System
- Notepad
- SiliConnect
- WireCarp
- MODsuit Control
- Spectre Meter
- Direct Messenger*
- LifeConnect
- Custodial Locator
- Fission360
- Camera
- Status Display
- SignalCommander

*By the by, sending messages has a cooldown, so it shouldn't be as
spammy. If it turns out to not be enough, I can make it so messages from
circuit will be ignored by other messenger circuits.

The PR is no longer WIP.

I believe modular computers could make for some interesting setups with
circuits, since they're fairly flexible and stocked with features unlike
many other appliances, therefore also a speck more abusable, though
limits, cooldowns, logging and sanitization have been implemented to
keep it in check.

🆑
add: Modular Computers now support integrated circuits. What can be done
with them depends on the programs installed and whether they're running
(open or background).
add: Modular Consoles (the machinery) now have a small backup cell they
draw power from if the power goes out.
/🆑

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
SkyratBot
2024-01-21 15:59:14 +01:00
committed by GitHub
parent ab5a4d0f99
commit fdcfabefd8
60 changed files with 1286 additions and 240 deletions
@@ -97,6 +97,7 @@
/datum/computer_file/program/ai_restorer/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_beginReconstruction")
if(!stored_card || !stored_card.AI)
@@ -29,7 +29,7 @@
traitor_data = null
return ..()
/datum/computer_file/program/contract_uplink/ui_act(action, params)
/datum/computer_file/program/contract_uplink/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -38,6 +38,7 @@
return ..()
/datum/computer_file/program/ntnet_dos/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_target_relay")
for(var/obj/machinery/ntnet_relay/relays as anything in SSmachines.get_machines_by_type(/obj/machinery/ntnet_relay))
@@ -38,6 +38,7 @@
spark_system.start()
/datum/computer_file/program/revelation/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_arm")
armed = !armed
@@ -107,7 +107,8 @@
data["BossID"] = "boss[boss_id].gif"
return data
/datum/computer_file/program/arcade/ui_act(action, list/params)
/datum/computer_file/program/arcade/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
usr.played_game()
var/gamerSkillLevel = 0
var/gamerSkill = 0
@@ -67,7 +67,8 @@
data["gasmixes"] = last_gasmix_data
return data
/datum/computer_file/program/atmosscan/ui_act(action, list/params)
/datum/computer_file/program/atmosscan/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("scantoggle")
if(atmozphere_mode == ATMOZPHERE_SCAN_CLICK)
@@ -10,6 +10,7 @@
size = 5
tgui_id = "NtosCyborgRemoteMonitor"
program_icon = "project-diagram"
circuit_comp_type = /obj/item/circuit_component/mod_program/borg_monitor
var/list/loglist = list() ///A list to copy a borg's IC log list into
var/mob/living/silicon/robot/DL_source ///reference of a borg if we're downloading a log, or null if not.
var/DL_progress = -1 ///Progress of current download, 0 to 100, -1 for no current download
@@ -105,29 +106,46 @@
return data
/datum/computer_file/program/borg_monitor/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("messagebot")
var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
if(!istype(R))
return TRUE
var/ID = checkID()
if(!ID)
return TRUE
if(R.stat == DEAD) //Dead borgs will listen to you no longer
to_chat(usr, span_warning("Error -- Could not open a connection to unit:[R]"))
var/message = tgui_input_text(usr, "Message to be sent to remote cyborg", "Send Message")
if(!message)
return TRUE
to_chat(R, "<br><br>[span_notice("Message from [ID] -- \"[message]\"")]<br>")
to_chat(usr, "Message sent to [R]: [message]")
R.logevent("Message from [ID] -- \"[message]\"")
SEND_SOUND(R, 'sound/machines/twobeep_high.ogg')
if(R.connected_ai)
to_chat(R.connected_ai, "<br><br>[span_notice("Message from [ID] to [R] -- \"[message]\"")]<br>")
SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg')
usr.log_talk(message, LOG_PDA, tag="Cyborg Monitor Program: ID name \"[ID]\" to [R]")
var/mob/living/silicon/robot/robot = locate(params["ref"]) in GLOB.silicon_mobs
message_robot(robot, usr)
return TRUE
/datum/computer_file/program/borg_monitor/proc/message_robot(mob/living/silicon/robot/robot, mob/user)
if(!istype(robot))
return TRUE
var/ID = checkID()
if(!ID)
return FALSE
if(robot.stat == DEAD) //Dead borgs will listen to you no longer
to_chat(user, span_warning("Error -- Could not open a connection to unit:[robot]"))
return FALSE
var/message = tgui_input_text(user, "Message to be sent to remote cyborg", "Send Message")
if(!message)
return FALSE
send_message(message, robot, user)
/datum/computer_file/program/borg_monitor/proc/send_message(message, mob/living/silicon/robot/robot, mob/user)
var/ID = checkID()
if(!ID)
return FALSE
if(robot.stat == DEAD) //Dead borgs will listen to you no longer
if(user)
to_chat(user, span_warning("Error -- Could not open a connection to unit:[robot]"))
return FALSE
to_chat(robot, "<br><br>[span_notice("Message from [ID] -- \"[message]\"")]<br>")
if(user)
to_chat(user, "Message sent to [robot]: [message]")
robot.logevent("Message from [ID] -- \"[message]\"")
SEND_SOUND(robot, 'sound/machines/twobeep_high.ogg')
if(robot.connected_ai)
to_chat(robot.connected_ai, "<br><br>[span_notice("Message from [ID] to [robot] -- \"[message]\"")]<br>")
SEND_SOUND(robot.connected_ai, 'sound/machines/twobeep_high.ogg')
user?.log_talk(message, LOG_PDA, tag = "Cyborg Monitor Program: ID name \"[ID]\" to [robot]")
return TRUE
///This proc is used to determin if a borg should be shown in the list (based on the borg's scrambledcodes var). Syndicate version overrides this to show only syndicate borgs.
/datum/computer_file/program/borg_monitor/proc/evaluate_borg(mob/living/silicon/robot/R)
if(!is_valid_z_level(get_turf(computer), get_turf(R)))
@@ -154,6 +172,7 @@
extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
program_flags = PROGRAM_ON_SYNDINET_STORE
download_access = list()
circuit_comp_type = /obj/item/circuit_component/mod_program/borg_monitor/syndie
/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R)
if(!is_valid_z_level(get_turf(computer), get_turf(R)))
@@ -164,3 +183,31 @@
/datum/computer_file/program/borg_monitor/syndicate/checkID()
return "\[CLASSIFIED\]" //no ID is needed for the syndicate version's message function, and the borg will see "[CLASSIFIED]" as the message sender.
/obj/item/circuit_component/mod_program/borg_monitor
associated_program = /datum/computer_file/program/borg_monitor
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
///Circuit input for the robot we want to message
var/datum/port/input/target_robot
///The message we want to send
var/datum/port/input/set_message
/obj/item/circuit_component/mod_program/borg_monitor/populate_ports()
. = ..()
target_robot = add_input_port("Receiver", PORT_TYPE_ATOM)
set_message = add_input_port("Set Message", PORT_TYPE_STRING, trigger = PROC_REF(sanitize_borg_message))
/obj/item/circuit_component/mod_program/borg_monitor/proc/sanitize_borg_message(datum/port/port)
set_message.set_value(trim(html_encode(set_message.value), MAX_MESSAGE_LEN))
/obj/item/circuit_component/mod_program/borg_monitor/input_received(datum/port/port)
if(!length(set_message.value) || !iscyborg(target_robot.value))
return
var/mob/living/silicon/robot/robot = target_robot.value
var/datum/computer_file/program/borg_monitor/monitor = associated_program
if(monitor.send_message(set_message.value, robot))
monitor.computer.log_talk("Cyborg Monitor message (ID name \"[monitor.checkID()]\") sent to [key_name(robot)] by [parent.get_creator()]: [set_message.value]")
/obj/item/circuit_component/mod_program/borg_monitor/syndie
associated_program = /datum/computer_file/program/borg_monitor/syndicate
@@ -57,7 +57,8 @@
data["bountyText"] = bounty_text
return data
/datum/computer_file/program/bounty_board/ui_act(action, list/params)
/datum/computer_file/program/bounty_board/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/current_ref_num = params["request"]
var/current_app_num = params["applicant"]
var/datum/bank_account/request_target
@@ -166,7 +166,8 @@
data["max_order"] = CARGO_MAX_ORDER
return data
/datum/computer_file/program/budgetorders/ui_act(action, params, datum/tgui/ui)
/datum/computer_file/program/budgetorders/ui_act(action, params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("send")
if(!SSshuttle.supply.canMove())
@@ -87,6 +87,7 @@
return ..()
/datum/computer_file/program/card_mod/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/user = usr
var/obj/item/card/id/inserted_auth_card = computer.computer_id_slot
@@ -26,7 +26,8 @@
data["barcode_split"] = cut_multiplier * 100
return data
/datum/computer_file/program/shipping/ui_act(action, list/params)
/datum/computer_file/program/shipping/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(!computer.computer_id_slot) //We need an ID to successfully run
return FALSE
@@ -49,6 +49,7 @@
return new_converstaion
/datum/computer_file/program/chatclient/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/datum/ntnet_conversation/channel = SSmodular_computers.get_chat_channel_by_id(active_channel)
var/authed = FALSE
if(channel && ((channel.channel_operator == src) || netadmin_mode))
@@ -44,7 +44,8 @@
data["valid_id"] = TRUE
return data
/datum/computer_file/program/coupon/ui_act(action, params, datum/tgui/ui)
/datum/computer_file/program/coupon/ui_act(action, params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/obj/item/card/id/user_id = computer.computer_id_slot
if(!(user_id?.registered_account.add_to_accounts))
return TRUE
@@ -16,7 +16,8 @@
data["manifest"] = GLOB.manifest.get_manifest()
return data
/datum/computer_file/program/crew_manifest/ui_act(action, params, datum/tgui/ui)
/datum/computer_file/program/crew_manifest/ui_act(action, params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_print")
if(computer) //This option should never be called if there is no printer
@@ -13,6 +13,7 @@
var/error
/datum/computer_file/program/filemanager/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_deletefile")
var/datum/computer_file/file = computer.find_file_by_name(params["name"])
@@ -162,6 +162,7 @@
return data
/datum/computer_file/program/scipaper_program/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("et_alia")
paper_to_be.et_alia = !paper_to_be.et_alia
@@ -53,7 +53,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
return FALSE
/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui)
/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/obj/item/card/id/user_id = computer.computer_id_slot
if(!user_id || !(ACCESS_CHANGE_IDS in user_id.access))
return TRUE
@@ -49,7 +49,8 @@
data += game.ui_assets(user)
return data
/datum/computer_file/program/mafia/ui_act(mob/user, params, datum/tgui/ui, datum/ui_state/state)
/datum/computer_file/program/mafia/ui_act(mob/user, params, datum/tgui/ui, datum/ui_state/state, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/datum/mafia_controller/game = GLOB.mafia_game
if(!game)
game = create_mafia_game()
@@ -8,6 +8,7 @@
can_run_on_flags = PROGRAM_PDA
tgui_id = "NtosCamera"
program_icon = "camera"
circuit_comp_type = /obj/item/circuit_component/mod_program/camera
/// Camera built-into the tablet.
var/obj/item/camera/internal_camera
@@ -16,6 +17,48 @@
/// How many pictures were taken already, used for the camera's TGUI photo display
var/picture_number = 1
/obj/item/circuit_component/mod_program/camera
associated_program = /datum/computer_file/program/maintenance/camera
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
///A target to take a picture of.
var/datum/port/input/picture_target
///The photographed target
var/datum/port/output/photographed
/obj/item/circuit_component/mod_program/camera/populate_ports()
. = ..()
picture_target = add_input_port("Picture Target", PORT_TYPE_ATOM)
photographed = add_output_port("Photographed Entity", PORT_TYPE_ATOM)
/obj/item/circuit_component/mod_program/camera/register_shell(atom/movable/shell)
. = ..()
var/datum/computer_file/program/maintenance/camera/cam = associated_program
RegisterSignal(cam.internal_camera, COMSIG_CAMERA_IMAGE_CAPTURED, PROC_REF(on_image_captured))
/obj/item/circuit_component/mod_program/camera/unregister_shell()
var/datum/computer_file/program/maintenance/camera/cam = associated_program
UnregisterSignal(cam.internal_camera, COMSIG_CAMERA_IMAGE_CAPTURED)
return ..()
/obj/item/circuit_component/mod_program/camera/input_received(datum/port/input/port)
var/atom/target = picture_target.value
if(!target)
var/turf/our_turf = get_location()
target = locate(our_turf.x, our_turf.y, our_turf.z)
if(!target)
return
var/datum/computer_file/program/maintenance/camera/cam = associated_program
if(!cam.internal_camera.can_target(target))
return
var/pic_size_x = cam.internal_camera.picture_size_x - 1
var/pic_size_y = cam.internal_camera.picture_size_y - 1
INVOKE_ASYNC(cam.internal_camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, null, pic_size_x, pic_size_y)
/obj/item/circuit_component/mod_program/camera/proc/on_image_captured(obj/item/camera/source, atom/target, mob/user)
SIGNAL_HANDLER
photographed.set_output(target)
/datum/computer_file/program/maintenance/camera/on_install()
. = ..()
internal_camera = new(computer)
@@ -50,13 +93,13 @@
return data
/datum/computer_file/program/maintenance/camera/ui_act(action, params, datum/tgui/ui)
var/mob/living/user = usr
/datum/computer_file/program/maintenance/camera/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("print_photo")
if(computer.stored_paper <= 0)
to_chat(usr, span_notice("Hardware error: Printer out of paper."))
return
internal_camera.printpicture(user, internal_picture)
internal_camera.printpicture(usr, internal_picture)
computer.stored_paper--
computer.visible_message(span_notice("\The [computer] prints out a paper."))
@@ -7,6 +7,7 @@
size = 2
tgui_id = "NtosMODsuit"
program_icon = "user-astronaut"
circuit_comp_type = /obj/item/circuit_component/mod_program/modsuit_control
///The suit we have control over.
var/obj/item/mod/control/controlled_suit
@@ -20,12 +21,15 @@
. = ..()
if(!istype(attacking_item, /obj/item/mod/control))
return FALSE
sync_modsuit(attacking_item, user)
return TRUE
/datum/computer_file/program/maintenance/modsuit_control/proc/sync_modsuit(obj/item/mod/control/new_modsuit, mob/living/user)
if(controlled_suit)
unsync_modsuit()
controlled_suit = attacking_item
controlled_suit = new_modsuit
RegisterSignal(controlled_suit, COMSIG_QDELETING, PROC_REF(unsync_modsuit))
user.balloon_alert(user, "suit updated")
return TRUE
user?.balloon_alert(user, "suit updated")
/datum/computer_file/program/maintenance/modsuit_control/proc/unsync_modsuit(atom/source)
SIGNAL_HANDLER
@@ -43,4 +47,26 @@
return controlled_suit?.ui_static_data()
/datum/computer_file/program/maintenance/modsuit_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
return controlled_suit?.ui_act(action, params, ui, state)
/obj/item/circuit_component/mod_program/modsuit_control
associated_program = /datum/computer_file/program/maintenance/modsuit_control
///Circuit port for loading a new suit to control
var/datum/port/input/suit_port
/obj/item/circuit_component/mod_program/modsuit_control/populate_ports()
. = ..()
suit_port = add_input_port("MODsuit Controlled", PORT_TYPE_ATOM)
/obj/item/circuit_component/mod_program/modsuit_control/input_received(datum/port/port)
var/datum/computer_file/program/maintenance/modsuit_control/control = associated_program
var/obj/item/mod/control/mod = suit_port.value
if(isnull(mod) && control.controlled_suit)
control.unsync_modsuit()
return
if(!istype(mod))
return
control.sync_modsuit(mod)
@@ -1,9 +1,9 @@
#define SPOOK_VALUE_SAME_TURF_MULT 1.5
#define SPOOK_VALUE_LIVING_MULT 6
#define SPOOK_VALUE_DEF_MOB 10
#define SPOOK_VALUE_ICON_STATE_MAX 120
#define SPOOK_VALUE_SEGMENT 15
#define SPOOK_COOLDOWN 2 SECONDS
/datum/computer_file/program/maintenance/spectre_meter
filename = "spectre_meter"
@@ -16,6 +16,7 @@
tgui_id = "NtosSpectreMeter"
program_icon = "ghost"
program_open_overlay = "spectre_meter_0"
circuit_comp_type = /obj/item/circuit_component/mod_program/spectre_meter
/// The cooldown for manual scans
COOLDOWN_DECLARE(manual_scan_cd)
/// Whether the automatic scan mode is active or not
@@ -46,13 +47,11 @@
return data
/datum/computer_file/program/maintenance/spectre_meter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("manual_scan")
if(COOLDOWN_FINISHED(src, manual_scan_cd))
INVOKE_ASYNC(src, PROC_REF(scan_surroundings))
COOLDOWN_START(src, manual_scan_cd, 2 SECONDS)
playsound(computer, 'sound/effects/ping_hit.ogg', vol = 40, vary = TRUE)
return TRUE
INVOKE_ASYNC(src, PROC_REF(scan_surroundings))
return TRUE
if("toggle_mode")
auto_mode = !auto_mode
if(auto_mode)
@@ -67,10 +66,13 @@
/datum/computer_file/program/maintenance/spectre_meter/process(seconds_per_tick)
if(auto_mode)
INVOKE_ASYNC(src, PROC_REF(scan_surroundings))
INVOKE_ASYNC(src, PROC_REF(scan_surroundings), FALSE)
///Return the "spook level" of the area the computer is in.
/datum/computer_file/program/maintenance/spectre_meter/proc/scan_surroundings()
/datum/computer_file/program/maintenance/spectre_meter/proc/scan_surroundings(manual = TRUE)
if(manual && !COOLDOWN_FINISHED(src, manual_scan_cd))
return
var/spook_value = 0
var/turf/turf = get_turf(computer)
@@ -103,6 +105,12 @@
if(program_open_overlay != old_open_overlay)
computer.update_appearance(UPDATE_OVERLAYS)
if(manual)
COOLDOWN_START(src, manual_scan_cd, SPOOK_COOLDOWN)
playsound(computer, 'sound/effects/ping_hit.ogg', vol = 40, vary = TRUE)
SEND_SIGNAL(computer, COMSIG_MODULAR_COMPUTER_SPECTRE_SCAN, last_spook_value)
/datum/looping_sound/spectre_meter
mid_sounds = /datum/looping_sound/geiger::mid_sounds
mid_length = 2
@@ -128,8 +136,41 @@
last_spook_value = 0
return ..()
/obj/item/circuit_component/mod_program/spectre_meter
associated_program = /datum/computer_file/program/maintenance/spectre_meter
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
/// Returns the spookiness of each scan.
var/datum/port/output/scan_results
/obj/item/circuit_component/mod_program/spectre_meter/populate_ports()
. = ..()
scan_results = add_output_port("Scan Results", PORT_TYPE_NUMBER)
/obj/item/circuit_component/mod_program/spectre_meter/register_shell(atom/movable/shell)
. = ..()
RegisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_SPECTRE_SCAN, PROC_REF(on_scan))
/obj/item/circuit_component/mod_program/spectre_meter/unregister_shell()
UnregisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_SPECTRE_SCAN)
return ..()
/obj/item/circuit_component/mod_program/spectre_meter/get_ui_notices()
. = ..()
. += create_ui_notice("Scan Coooldown: [SPOOK_COOLDOWN]", "orange", "stopwatch")
/obj/item/circuit_component/mod_program/spectre_meter/input_received(datum/port/port)
var/datum/computer_file/program/maintenance/spectre_meter/meter = associated_program
INVOKE_ASYNC(meter, TYPE_PROC_REF(/datum/computer_file/program/maintenance/spectre_meter, scan_surroundings))
/obj/item/circuit_component/mod_program/spectre_meter/proc/on_scan(datum/source, spook_value)
SIGNAL_HANDLER
scan_results.set_value(spook_value)
#undef SPOOK_VALUE_SAME_TURF_MULT
#undef SPOOK_VALUE_LIVING_MULT
#undef SPOOK_VALUE_DEF_MOB
#undef SPOOK_VALUE_ICON_STATE_MAX
#undef SPOOK_VALUE_SEGMENT
#undef SPOOK_COOLDOWN
@@ -26,6 +26,7 @@
///Called post-installation of an application in a computer, after 'computer' var is set.
/datum/computer_file/program/maintenance/theme/on_install()
SHOULD_CALL_PARENT(FALSE)
//add the theme to the computer and increase its size to match
var/datum/computer_file/program/themeify/theme_app = locate() in computer.stored_files
if(theme_app)
@@ -0,0 +1,111 @@
#define MESSENGER_CIRCUIT_MIN_COOLDOWN 5 SECONDS
#define MESSENGER_CIRCUIT_MAX_COOLDOWN 45 SECONDS
#define MESSENGER_CIRCUIT_CD_PER_RECIPIENT 1.5 SECONDS
/obj/item/circuit_component/mod_program/messenger
associated_program = /datum/computer_file/program/messenger
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
///Contents of the last received message
var/datum/port/output/received_message
///Name of the sender of the above
var/datum/port/output/sender_name
///Job title of the sender of the above
var/datum/port/output/sender_job
///Reference to the device that sent the message. Usually a PDA.
var/datum/port/output/sender_device
///A message to be sent when triggered
var/datum/port/input/message
///A list of PDA targets for the message to be sent
var/datum/port/input/targets
///Plays the ringtone when the input is received
var/datum/port/input/ring
///Set the ringtone to the input
var/datum/port/input/set_ring
/obj/item/circuit_component/mod_program/messenger/populate_ports()
. = ..()
received_message = add_output_port("Received Message", PORT_TYPE_STRING)
sender_name = add_output_port("Sender Name", PORT_TYPE_STRING)
sender_job = add_output_port("Sender Job", PORT_TYPE_STRING)
sender_device = add_output_port("Sender Device", PORT_TYPE_ATOM)
message = add_input_port("Message", PORT_TYPE_STRING)
targets = add_input_port("Targets", PORT_TYPE_LIST(PORT_TYPE_ATOM))
ring = add_input_port("Play Ringtone", PORT_TYPE_SIGNAL, trigger = PROC_REF(play_ringtone))
set_ring = add_input_port("Set Ringtone", PORT_TYPE_STRING, trigger = PROC_REF(set_ringtone))
///Sanitize the targets list so it doesn't contain duplicate targets and our own computer.
/obj/item/circuit_component/mod_program/messenger/pre_input_received(datum/port/port)
if(COMPONENT_TRIGGERED_BY(targets, port))
targets.value = unique_list(targets.value) - associated_program.computer
/obj/item/circuit_component/mod_program/messenger/input_received(datum/port/port)
var/list/messenger_targets = list()
for(var/obj/item/modular_computer/modpc in targets.value)
var/datum/computer_file/program/messenger/messenger = locate() in modpc.stored_files
if(messenger)
messenger_targets |= messenger
var/messenger_length = length(messenger_targets)
if(!messenger_length)
return
var/datum/computer_file/program/messenger/messenger = associated_program
if(is_ic_filtered_for_pdas(message.value) || is_soft_ic_filtered_for_pdas(message.value))
return
///We need to async send_message() because some tcomms devices might sleep. Also because of (non-existent) user tgui alerts.
INVOKE_ASYNC(messenger, TYPE_PROC_REF(/datum/computer_file/program/messenger, send_message), src, message.value, messenger_targets)
/obj/item/circuit_component/mod_program/messenger/register_shell(atom/movable/shell)
. = ..()
RegisterSignal(associated_program.computer, COMSIG_MODULAR_PDA_MESSAGE_RECEIVED, PROC_REF(message_received))
RegisterSignal(associated_program.computer, COMSIG_MODULAR_PDA_MESSAGE_SENT, PROC_REF(message_sent))
/obj/item/circuit_component/mod_program/messenger/unregister_shell()
UnregisterSignal(associated_program.computer, list(COMSIG_MODULAR_PDA_MESSAGE_RECEIVED, COMSIG_MODULAR_PDA_MESSAGE_SENT))
return ..()
/obj/item/circuit_component/mod_program/messenger/get_ui_notices()
. = ..()
. += create_ui_notice("Cooldown per recipient: [DisplayTimeText(MESSENGER_CIRCUIT_CD_PER_RECIPIENT)]", "orange", "stopwatch")
. += create_ui_notice("Minimum cooldown: [DisplayTimeText(MESSENGER_CIRCUIT_MIN_COOLDOWN)]", "orange", "stopwatch")
. += create_ui_notice("Maximum cooldown: [DisplayTimeText(MESSENGER_CIRCUIT_MAX_COOLDOWN)]", "orange", "stopwatch")
/obj/item/circuit_component/mod_program/messenger/proc/message_received(datum/source, datum/signal/subspace/messaging/tablet_message/signal, message_job, message_name)
SIGNAL_HANDLER
received_message.set_value(signal.data["message"])
sender_name.set_value(message_name)
sender_job.set_value(message_job)
var/atom/source_device
if(istype(signal.source, /datum/computer_file/program/messenger))
var/datum/computer_file/program/messenger/sender_messenger = source
source_device = sender_messenger.computer
else if(isatom(signal.source))
source_device = signal.source
sender_device.set_value(source_device)
///Set the cooldown after the message was sent (by us)
/obj/item/circuit_component/mod_program/messenger/proc/message_sent(datum/source, atom/origin, datum/signal/subspace/messaging/tablet_message/signal)
SIGNAL_HANDLER
if(origin != src)
return
var/targets_length = length(signal.data["targets"])
var/datum/computer_file/program/messenger/messenger = associated_program
var/cool = clamp(targets_length * MESSENGER_CIRCUIT_CD_PER_RECIPIENT, MESSENGER_CIRCUIT_MIN_COOLDOWN, MESSENGER_CIRCUIT_MAX_COOLDOWN)
COOLDOWN_START(messenger, last_text, cool)
/obj/item/circuit_component/mod_program/messenger/proc/set_ringtone(datum/port/port)
var/datum/computer_file/program/messenger/messenger = associated_program
messenger.set_ringtone(set_ring.value)
/obj/item/circuit_component/mod_program/messenger/proc/play_ringtone(datum/port/port)
var/datum/computer_file/program/messenger/messenger = associated_program
messenger.computer.ring(messenger.ringtone)
#undef MESSENGER_CIRCUIT_MIN_COOLDOWN
#undef MESSENGER_CIRCUIT_MAX_COOLDOWN
#undef MESSENGER_CIRCUIT_CD_PER_RECIPIENT
@@ -14,12 +14,13 @@
size = 0
undeletable = TRUE // It comes by default in tablets, can't be downloaded, takes no space and should obviously not be able to be deleted.
power_cell_use = NONE
program_flags = PROGRAM_HEADER | PROGRAM_RUNS_WITHOUT_POWER
program_flags = PROGRAM_HEADER | PROGRAM_RUNS_WITHOUT_POWER | PROGRAM_CIRCUITS_RUN_WHEN_CLOSED
can_run_on_flags = PROGRAM_PDA
ui_header = "ntnrc_idle.gif"
tgui_id = "NtosMessenger"
program_icon = "comment-alt"
alert_able = TRUE
circuit_comp_type = /obj/item/circuit_component/mod_program/messenger
/// Whether the user is invisible to the message list.
var/invisible = FALSE
@@ -138,6 +139,18 @@
for(var/datum/tgui/window as anything in computer.open_uis)
SSassets.transport.send_assets(window.user, data)
/// Set the ringtone if possible. Also handles encoding.
/datum/computer_file/program/messenger/proc/set_ringtone(new_ringtone, mob/user)
new_ringtone = trim(html_encode(new_ringtone), MESSENGER_RINGTONE_MAX_LENGTH)
if(!new_ringtone)
return FALSE
if(SEND_SIGNAL(computer, COMSIG_TABLET_CHANGE_ID, user, new_ringtone) & COMPONENT_STOP_RINGTONE_CHANGE)
return FALSE
ringtone = ringtone
return TRUE
/datum/computer_file/program/messenger/ui_interact(mob/user, datum/tgui/ui)
var/list/data = get_picture_assets()
SSassets.transport.send_assets(user, data)
@@ -147,19 +160,15 @@
return GLOB.reverse_contained_state
return GLOB.default_state
/datum/computer_file/program/messenger/ui_act(action, list/params, datum/tgui/ui)
/datum/computer_file/program/messenger/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PDA_ringSet")
var/new_ringtone = tgui_input_text(usr, "Enter a new ringtone", "Ringtone", ringtone, MESSENGER_RINGTONE_MAX_LENGTH)
var/mob/living/usr_mob = usr
if(!new_ringtone || !in_range(computer, usr_mob) || computer.loc != usr_mob)
var/mob/living/user = usr
var/new_ringtone = tgui_input_text(user, "Enter a new ringtone", "Ringtone", ringtone, encode = FALSE)
if(!in_range(computer, user) || computer.loc != user)
return FALSE
if(SEND_SIGNAL(computer, COMSIG_TABLET_CHANGE_ID, usr_mob, new_ringtone) & COMPONENT_STOP_RINGTONE_CHANGE)
return FALSE
ringtone = new_ringtone
return TRUE
return set_ringtone(new_ringtone, user)
if("PDA_toggleAlerts")
alert_silenced = !alert_silenced
@@ -451,18 +460,23 @@
message = emoji_sanitize(message)
// check message against filter
if(!check_pda_message_against_filter(message, sender))
if(sender && !check_pda_message_against_filter(message, sender))
return null
return message
/// Sends a message to targets via PDA. When sending to everyone, set `everyone` to true so the message is formatted accordingly
/datum/computer_file/program/messenger/proc/send_message(mob/living/sender, message, list/targets, everyone = FALSE)
/datum/computer_file/program/messenger/proc/send_message(atom/source, message, list/targets, everyone = FALSE)
var/mob/living/sender
if(isliving(source))
sender = source
else if(is_ic_filtered_for_pdas(message) || is_soft_ic_filtered_for_pdas(message))
return FALSE
message = sanitize_pda_message(message, sender)
if(!message)
return FALSE
// upgrade the image asset to a permanent key
var/photo_asset_key = selected_image
if(photo_asset_key == TEMP_IMAGE_PATH(REF(src)))
@@ -474,7 +488,7 @@
var/list/datum/computer_file/program/messenger/target_messengers = list()
var/list/datum/pda_chat/target_chats = list()
var/should_alert = length(targets) == 1
var/should_alert = length(targets) == 1 && sender
// filter out invalid targets
for(var/target in targets)
@@ -523,7 +537,7 @@
target_chats += target_chat
target_messengers += target_messenger
if(!send_message_signal(sender, message, target_messengers, photo_asset_key, everyone))
if(!send_message_signal(source, message, target_messengers, photo_asset_key, everyone))
return FALSE
// Log it in our logs
@@ -553,8 +567,11 @@
return send_message_signal(sender, message, targets, fake_photo, FALSE, TRUE, fake_name, fake_job)
/datum/computer_file/program/messenger/proc/send_message_signal(mob/sender, message, list/datum/computer_file/program/messenger/targets, photo_path = null, everyone = FALSE, rigged = FALSE, fake_name = null, fake_job = null)
if(!sender.can_perform_action(computer, ALLOW_RESTING))
/datum/computer_file/program/messenger/proc/send_message_signal(atom/source, message, list/datum/computer_file/program/messenger/targets, photo_path = null, everyone = FALSE, rigged = FALSE, fake_name = null, fake_job = null)
var/mob/sender
if(ismob(source))
sender = source
if(sender && !sender.can_perform_action(computer, ALLOW_RESTING))
return FALSE
if(!COOLDOWN_FINISHED(src, last_text))
@@ -566,7 +583,8 @@
// check for jammers
if(is_within_radio_jammer_range(computer) && !rigged)
// different message so people know it's a radio jammer
to_chat(sender, span_notice("ERROR: Network unavailable, please try again later."))
if(sender)
to_chat(sender, span_notice("ERROR: Network unavailable, please try again later."))
if(alert_able && !alert_silenced)
playsound(computer, 'sound/machines/terminal_error.ogg', 15, TRUE)
return FALSE
@@ -596,38 +614,46 @@
// If it didn't reach, note that fact
if (!signal.data["done"])
to_chat(sender, span_notice("ERROR: Server is not responding."))
if(sender)
to_chat(sender, span_notice("ERROR: Server is not responding."))
if(alert_able && !alert_silenced)
playsound(computer, 'sound/machines/terminal_error.ogg', 15, TRUE)
return FALSE
// SKYRAT EDIT BEGIN - PDA messages show a visible message; again!
sender.visible_message(span_notice("[sender]'s PDA rings out with the soft sound of keypresses"), vision_distance = COMBAT_MESSAGE_RANGE)
// SKYRAT EDIT END
var/shell_addendum = ""
if(istype(source, /obj/item/circuit_component))
var/obj/item/circuit_component/circuit = source
shell_addendum = "[circuit.parent.get_creator()] "
// Log in the talk log
sender.log_talk(message, LOG_PDA, tag="[rigged ? "Rigged" : ""] PDA: [computer.saved_identification] to [signal.format_target()]")
source.log_talk(message, LOG_PDA, tag="[shell_addendum][rigged ? "Rigged" : ""] PDA: [computer.saved_identification] to [signal.format_target()]")
if(rigged)
log_bomber(sender, "sent a rigged PDA message (Name: [fake_name]. Job: [fake_job]) to [english_list(stringified_targets)] [!is_special_character(sender) ? "(SENT BY NON-ANTAG)" : ""]")
message = emoji_parse(message) //already sent- this just shows the sent emoji as one to the sender in the to_chat
// Show it to ghosts
var/ghost_message = span_game_say("[span_name("[sender]")] [rigged ? "(as [span_name(fake_name)]) Rigged " : ""]PDA Message --> [span_name("[signal.format_target()]")]: \"[signal.format_message()]\"")
var/ghost_message = span_game_say("[span_name("[source]")] [rigged ? "(as [span_name(fake_name)]) Rigged " : ""]PDA Message --> [span_name("[signal.format_target()]")]: \"[signal.format_message()]\"")
var/list/message_listeners = GLOB.dead_player_list + GLOB.current_observers_list
for(var/mob/listener as anything in message_listeners)
if(!(get_chat_toggles(listener) & CHAT_GHOSTPDA))
continue
to_chat(listener, "[FOLLOW_LINK(listener, sender)] [ghost_message]")
to_chat(listener, "[FOLLOW_LINK(listener, source)] [ghost_message]")
to_chat(sender, span_info("PDA message sent to [signal.format_target()]: \"[message]\""))
if(sender)
to_chat(sender, span_info("PDA message sent to [signal.format_target()]: \"[message]\""))
if (alert_able && !alert_silenced)
computer.send_sound()
COOLDOWN_START(src, last_text, 1 SECONDS)
SEND_SIGNAL(computer, COMSIG_MODULAR_PDA_MESSAGE_SENT, source, signal)
selected_image = null
return TRUE
@@ -642,6 +668,7 @@
var/sender_ref = signal.data["ref"]
// don't create a new chat for rigged messages, make it a one off notif
if(!is_rigged)
var/datum/pda_message/message = new(signal.data["message"], FALSE, station_time_timestamp(PDA_MESSAGE_TIMESTAMP_FORMAT), signal.data["photo"], signal.data["everyone"])
@@ -662,6 +689,14 @@
if(computer.loc && isliving(computer.loc))
receievers += computer.loc
// resolving w/o nullcheck here, assume the messenger exists if a real person sent a message
var/datum/computer_file/program/messenger/sender_messenger = chat.recipient?.resolve()
var/sender_title = is_fake_user ? STRINGIFY_PDA_TARGET(fake_name, fake_job) : get_messenger_name(sender_messenger)
var/sender_name = is_fake_user ? fake_name : sender_messenger.computer.saved_identification
SEND_SIGNAL(computer, COMSIG_MODULAR_PDA_MESSAGE_RECEIVED, signal, fake_job || sender_messenger?.computer.saved_job , sender_name)
for(var/mob/living/messaged_mob as anything in receievers)
if(messaged_mob.stat >= UNCONSCIOUS)
continue
@@ -675,11 +710,6 @@
else
reply = "(<a href='byond://?src=[REF(src)];choice=[reply_href];skiprefresh=1;target=[REF(chat)]'>Reply</a>)"
// resolving w/o nullcheck here, assume the messenger exists if a real person sent a message
var/datum/computer_file/program/messenger/sender_messenger = chat.recipient?.resolve()
var/sender_title = is_fake_user ? STRINGIFY_PDA_TARGET(fake_name, fake_job) : get_messenger_name(sender_messenger)
var/sender_name = is_fake_user ? fake_name : sender_messenger.computer.saved_identification
if (isAI(messaged_mob))
sender_title = "<a href='?src=[REF(messaged_mob)];track=[html_encode(sender_name)]'>[sender_title]</a>"
@@ -27,4 +27,5 @@
return newscaster_ui.ui_static_data(user)
/datum/computer_file/program/newscaster/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
return newscaster_ui.ui_act(action, params, ui, state)
@@ -8,6 +8,7 @@
tgui_id = "NtosNotepad"
program_icon = "book"
can_run_on_flags = PROGRAM_ALL
circuit_comp_type = /obj/item/circuit_component/mod_program/notepad
var/written_note = "Congratulations on your station upgrading to the new NtOS and Thinktronic based collaboration effort, \
bringing you the best in electronics and software since 2467!\n\
@@ -19,7 +20,8 @@
Quarter - Either sides of Aft\n\
Bow - Either sides of Fore"
/datum/computer_file/program/notepad/ui_act(action, list/params, datum/tgui/ui)
/datum/computer_file/program/notepad/ui_act(action, list/params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("UpdateNote")
written_note = params["newnote"]
@@ -31,3 +33,34 @@
data["note"] = written_note
return data
/obj/item/circuit_component/mod_program/notepad
associated_program = /datum/computer_file/program/notepad
///When the input is received, the written note will be set to its value.
var/datum/port/input/set_text
///The written note output, sent everytime notes are updated.
var/datum/port/output/updated_text
/obj/item/circuit_component/mod_program/notepad/populate_ports()
. = ..()
set_text = add_input_port("Set Notes", PORT_TYPE_STRING)
updated_text = add_output_port("Updated Notes", PORT_TYPE_STRING)
/obj/item/circuit_component/mod_program/notepad/register_shell(atom/movable/shell)
. = ..()
RegisterSignal(associated_program, COMSIG_UI_ACT, PROC_REF(on_note_updated))
/obj/item/circuit_component/mod_program/notepad/unregister_shell()
UnregisterSignal(associated_program, COMSIG_UI_ACT)
return ..()
/obj/item/circuit_component/mod_program/notepad/proc/on_note_updated(datum/source, mob/user, action, list/params)
SIGNAL_HANDLER
if(action == "UpdateNote")
updated_text.set_output(params["newnote"])
/obj/item/circuit_component/mod_program/notepad/input_received(datum/port/port)
var/datum/computer_file/program/notepad/pad = associated_program
pad.written_note = set_text.value
SStgui.update_uis(pad.computer)
updated_text.set_output(pad.written_note)
@@ -1,3 +1,10 @@
#define NT_PAY_STATUS_NO_ACCOUNT 0
#define NT_PAY_STATUS_DEPT_ACCOUNT 1
#define NT_PAY_STATUS_INVALID_TOKEN 2
#define NT_PAY_SATUS_SENDER_IS_RECEIVER 3
#define NT_PAY_STATUS_INVALID_MONEY 4
#define NT_PAY_STATUS_SUCCESS 5
/datum/computer_file/program/nt_pay
filename = "ntpay"
filedesc = "Nanotrasen Pay System"
@@ -8,45 +15,19 @@
tgui_id = "NtosPay"
program_icon = "money-bill-wave"
can_run_on_flags = PROGRAM_ALL
circuit_comp_type = /obj/item/circuit_component/mod_program/nt_pay
///Reference to the currently logged in user.
var/datum/bank_account/current_user
///Pay token, by which we can send credits
var/token
///Amount of credits, which we sends
var/money_to_send = 0
///Pay token what we want to find
var/wanted_token
/datum/computer_file/program/nt_pay/ui_act(action, list/params, datum/tgui/ui)
/datum/computer_file/program/nt_pay/ui_act(action, list/params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("Transaction")
if(IS_DEPARTMENTAL_ACCOUNT(current_user))
return to_chat(usr, span_notice("The app is unable to withdraw from that card."))
token = params["token"]
money_to_send = params["amount"]
var/datum/bank_account/recipient
if(!token)
return to_chat(usr, span_notice("You need to enter your transfer target's pay token."))
if(!money_to_send)
return to_chat(usr, span_notice("You need to specify how much you're sending."))
if(token == current_user.pay_token)
return to_chat(usr, span_notice("You can't send credits to yourself."))
for(var/account as anything in SSeconomy.bank_accounts_by_id)
var/datum/bank_account/acc = SSeconomy.bank_accounts_by_id[account]
if(acc.pay_token == token)
recipient = acc
break
if(!recipient)
return to_chat(usr, span_notice("The app can't find who you're trying to pay. Did you enter the pay token right?"))
if(!current_user.has_money(money_to_send) || money_to_send < 1)
return current_user.bank_card_talk("You cannot afford it.")
recipient.bank_card_talk("You received [money_to_send] credit(s). Reason: transfer from [current_user.account_holder]")
recipient.transfer_money(current_user, money_to_send)
current_user.bank_card_talk("You send [money_to_send] credit(s) to [recipient.account_holder]. Now you have [current_user.account_balance] credit(s)")
var/token = params["token"]
var/money_to_send = params["amount"]
make_payment(token, money_to_send, usr)
if("GetPayToken")
wanted_token = null
@@ -58,8 +39,6 @@
if(!wanted_token)
return wanted_token = "Account \"[params["wanted_name"]]\" not found."
/datum/computer_file/program/nt_pay/ui_data(mob/user)
var/list/data = list()
@@ -74,3 +53,99 @@
data["transaction_list"] = current_user.transaction_history
return data
///Wrapper and signal for the main payment function of this program
/datum/computer_file/program/nt_pay/proc/make_payment(token, money_to_send, mob/user)
var/payment_result = _pay(token, money_to_send, user)
SEND_SIGNAL(computer, COMSIG_MODULAR_COMPUTER_NT_PAY_RESULT, payment_result)
/datum/computer_file/program/nt_pay/proc/_pay(token, money_to_send, mob/user)
if(IS_DEPARTMENTAL_ACCOUNT(current_user))
if(user)
to_chat(user, span_notice("The app is unable to withdraw from that card."))
return NT_PAY_STATUS_DEPT_ACCOUNT
var/datum/bank_account/recipient
if(!token)
if(user)
to_chat(user, span_notice("You need to enter your transfer target's pay token."))
return NT_PAY_STATUS_INVALID_TOKEN
if(money_to_send <= 0)
if(user)
to_chat(user, span_notice("You need to specify how much you're sending."))
return NT_PAY_STATUS_INVALID_MONEY
if(token == current_user.pay_token)
if(user)
to_chat(user, span_notice("You can't send credits to yourself."))
return NT_PAY_SATUS_SENDER_IS_RECEIVER
for(var/account as anything in SSeconomy.bank_accounts_by_id)
var/datum/bank_account/acc = SSeconomy.bank_accounts_by_id[account]
if(acc.pay_token == token)
recipient = acc
break
if(!recipient)
if(user)
to_chat(user, span_notice("The app can't find who you're trying to pay. Did you enter the pay token right?"))
return NT_PAY_STATUS_INVALID_TOKEN
if(!current_user.has_money(money_to_send) || money_to_send < 1)
current_user.bank_card_talk("You cannot afford it.")
return NT_PAY_STATUS_INVALID_MONEY
recipient.bank_card_talk("You received [money_to_send] credit(s). Reason: transfer from [current_user.account_holder]")
recipient.transfer_money(current_user, money_to_send)
current_user.bank_card_talk("You send [money_to_send] credit(s) to [recipient.account_holder]. Now you have [current_user.account_balance] credit(s)")
return NT_PAY_STATUS_SUCCESS
/obj/item/circuit_component/mod_program/nt_pay
associated_program = /datum/computer_file/program/nt_pay
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
///Circuit variables. This one is for the token we want to pay
var/datum/port/input/token_port
///The port for the money to send
var/datum/port/input/money_port
///Let's us know if the payment has gone through or not.
var/datum/port/output/payment_status
/obj/item/circuit_component/mod_program/nt_pay/register_shell(atom/movable/shell)
. = ..()
RegisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_NT_PAY_RESULT, PROC_REF(on_payment_result))
/obj/item/circuit_component/mod_program/nt_pay/unregister_shell()
UnregisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_NT_PAY_RESULT)
return ..()
/obj/item/circuit_component/mod_program/nt_pay/populate_ports()
. = ..()
token_port = add_input_port("Token", PORT_TYPE_STRING)
money_port = add_input_port("Amount", PORT_TYPE_NUMBER)
payment_status = add_output_port("Status", PORT_TYPE_NUMBER)
/obj/item/circuit_component/mod_program/nt_pay/get_ui_notices()
. = ..()
. += create_ui_notice("NT-Pay Statuses:")
. += create_ui_notice("Fail (No Account) - [NT_PAY_STATUS_NO_ACCOUNT]", "red")
. += create_ui_notice("Fail (Dept Account) - [NT_PAY_STATUS_DEPT_ACCOUNT]", "red")
. += create_ui_notice("Fail (Invalid Token) - [NT_PAY_STATUS_INVALID_TOKEN]", "red")
. += create_ui_notice("Fail (Sender = Receiver) - [NT_PAY_SATUS_SENDER_IS_RECEIVER]", "red")
. += create_ui_notice("Fail (Invalid Amount) - [NT_PAY_STATUS_INVALID_MONEY]", "red")
. += create_ui_notice("Success - [NT_PAY_STATUS_SUCCESS]", "green")
/obj/item/circuit_component/mod_program/nt_pay/input_received(datum/port/port)
var/datum/computer_file/program/nt_pay/program = associated_program
program.make_payment(token_port.value, money_port.value)
/obj/item/circuit_component/mod_program/nt_pay/proc/on_payment_result(datum/source, payment_result)
SIGNAL_HANDLER
payment_status.set_output(payment_result)
#undef NT_PAY_STATUS_NO_ACCOUNT
#undef NT_PAY_STATUS_DEPT_ACCOUNT
#undef NT_PAY_STATUS_INVALID_TOKEN
#undef NT_PAY_SATUS_SENDER_IS_RECEIVER
#undef NT_PAY_STATUS_INVALID_MONEY
#undef NT_PAY_STATUS_SUCCESS
@@ -105,6 +105,7 @@
download_completion += download_netspeed
/datum/computer_file/program/ntnetdownload/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_downloadfile")
if(!downloaded_file)
@@ -44,6 +44,7 @@
)
/datum/computer_file/program/portrait_printer/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("search")
if(search_string != params["to_search"])
@@ -1,3 +1,13 @@
///The selected target is not trackable
#define RADAR_NOT_TRACKABLE 0
///The selected target is trackable
#define RADAR_TRACKABLE 1
///The selected target is trackable, even if subtypes would normally consider it untrackable.
#define RADAR_TRACKABLE_ANYWAY 2
///If the target is something it shouldn't be normally tracking, this is the maximum distance within with it an be tracked.
#define MAX_RADAR_CIRCUIT_DISTANCE 18
/datum/computer_file/program/radar //generic parent that handles most of the process
filename = "genericfinder"
filedesc = "debug_finder"
@@ -58,12 +68,13 @@
return data
/datum/computer_file/program/radar/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("selecttarget")
var/selected_new_ref = params["ref"]
if(selected_new_ref in trackable_object_refs())
selected = selected_new_ref
SEND_SIGNAL(computer, COMSIG_MODULAR_COMPUTER_RADAR_SELECTED, selected)
return TRUE
if("scan")
@@ -133,13 +144,22 @@
**arg1 is the atom being evaluated.
*/
/datum/computer_file/program/radar/proc/trackable(atom/movable/signal)
if(!signal || !computer)
return FALSE
SHOULD_CALL_PARENT(TRUE)
if(isnull(signal) || isnull(computer))
return RADAR_NOT_TRACKABLE
var/turf/here = get_turf(computer)
var/turf/there = get_turf(signal)
if(!here || !there)
return FALSE //I was still getting a runtime even after the above check while scanning, so fuck it
return (there.z == here.z) || (is_station_level(here.z) && is_station_level(there.z))
return RADAR_NOT_TRACKABLE //I was still getting a runtime even after the above check while scanning, so fuck it
if(there.z != here.z && (!is_station_level(here.z) || !is_station_level(there.z)))
return RADAR_NOT_TRACKABLE
var/trackable_signal = SEND_SIGNAL(computer, COMSIG_MODULAR_COMPUTER_RADAR_TRACKABLE, signal, here, there)
switch(trackable_signal)
if(COMPONENT_RADAR_TRACK_ANYWAY)
return RADAR_TRACKABLE_ANYWAY
if(COMPONENT_RADAR_DONT_TRACK)
return RADAR_NOT_TRACKABLE
return RADAR_TRACKABLE
/**
*
@@ -168,7 +188,10 @@
*return an atom reference.
*/
/datum/computer_file/program/radar/proc/find_atom()
return
SHOULD_CALL_PARENT(TRUE)
var/list/atom_container = list(null)
SEND_SIGNAL(computer, COMSIG_MODULAR_COMPUTER_RADAR_FIND_ATOM, atom_container)
return atom_container[1]
//We use SSfastprocess for the program icon state because it runs faster than process_tick() does.
/datum/computer_file/program/radar/process()
@@ -221,9 +244,10 @@
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_MEDICAL)
program_icon = "heartbeat"
circuit_comp_type = /obj/item/circuit_component/mod_program/radar/medical
/datum/computer_file/program/radar/lifeline/find_atom()
return locate(selected) in GLOB.human_list
return ..() || (locate(selected) in GLOB.human_list)
/datum/computer_file/program/radar/lifeline/scan()
objects = list()
@@ -243,14 +267,16 @@
objects += list(crewinfo)
/datum/computer_file/program/radar/lifeline/trackable(mob/living/carbon/human/humanoid)
. = ..()
if(. == RADAR_TRACKABLE_ANYWAY)
return RADAR_TRACKABLE_ANYWAY
if(!humanoid || !istype(humanoid))
return FALSE
if(..())
if (istype(humanoid.w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/uniform = humanoid.w_uniform
if(uniform.has_sensor && uniform.sensor_mode >= SENSOR_COORDS) // Suit sensors must be on maximum
return TRUE
return FALSE
return RADAR_NOT_TRACKABLE
if(!istype(humanoid.w_uniform, /obj/item/clothing/under))
return RADAR_NOT_TRACKABLE
var/obj/item/clothing/under/uniform = humanoid.w_uniform
if(uniform.has_sensor && uniform.sensor_mode >= SENSOR_COORDS) // Suit sensors must be on maximum
return RADAR_TRACKABLE
///Tracks all janitor equipment
/datum/computer_file/program/radar/custodial_locator
@@ -262,9 +288,10 @@
program_icon = "broom"
size = 2
detomatix_resistance = DETOMATIX_RESIST_MINOR
circuit_comp_type = /obj/item/circuit_component/mod_program/radar/janitor
/datum/computer_file/program/radar/custodial_locator/find_atom()
return locate(selected) in GLOB.janitor_devices
return ..() || (locate(selected) in GLOB.janitor_devices)
/datum/computer_file/program/radar/custodial_locator/scan()
objects = list()
@@ -306,6 +333,7 @@
program_icon = "bomb"
arrowstyle = "ntosradarpointerS.png"
pointercolor = "red"
circuit_comp_type = /obj/item/circuit_component/mod_program/radar/nukie
/datum/computer_file/program/radar/fission360/on_start(mob/living/user)
. = ..()
@@ -323,7 +351,7 @@
return ..()
/datum/computer_file/program/radar/fission360/find_atom()
return SSpoints_of_interest.get_poi_atom_by_ref(selected)
return ..() || SSpoints_of_interest.get_poi_atom_by_ref(selected)
/datum/computer_file/program/radar/fission360/scan()
objects = list()
@@ -379,3 +407,129 @@
span_danger("[computer] vibrates and lets out an ominous alarm. Uh oh."),
span_notice("[computer] begins to vibrate rapidly. Wonder what that means..."),
)
/**
* Base circuit for the radar program.
* The abstract radar doesn't have this, nor this one is associated to it, so
* make sure to specify the associate_program and circuit_comp_type of subtypes,
*/
/obj/item/circuit_component/mod_program/radar
///The target to track
var/datum/port/input/target
///The selected target, from the app
var/datum/port/output/selected_by_app
/// The result from the output
var/datum/port/output/x_pos
var/datum/port/output/y_pos
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/mod_program/radar/populate_ports()
. = ..()
target = add_input_port("Target", PORT_TYPE_ATOM)
selected_by_app = add_output_port("Selected From Program", PORT_TYPE_ATOM)
x_pos = add_output_port("X", PORT_TYPE_NUMBER)
y_pos = add_output_port("Y", PORT_TYPE_NUMBER)
/obj/item/circuit_component/mod_program/radar/register_shell(atom/movable/shell)
. = ..()
RegisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_RADAR_TRACKABLE, PROC_REF(can_track))
RegisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_RADAR_FIND_ATOM, PROC_REF(get_atom))
RegisterSignal(associated_program.computer, COMSIG_MODULAR_COMPUTER_RADAR_SELECTED, PROC_REF(on_selected))
/obj/item/circuit_component/mod_program/radar/unregister_shell()
UnregisterSignal(associated_program.computer, list(
COMSIG_MODULAR_COMPUTER_RADAR_TRACKABLE,
COMSIG_MODULAR_COMPUTER_RADAR_FIND_ATOM,
COMSIG_MODULAR_COMPUTER_RADAR_SELECTED,
))
return ..()
/obj/item/circuit_component/mod_program/radar/get_ui_notices()
. = ..()
. += create_ui_notice("Max range for unsupported entities: [MAX_RADAR_CIRCUIT_DISTANCE] tiles", "orange", FA_ICON_BULLSEYE)
///Set the selected ref of the program to the target (if it exists) and update the x/y pos ports (if trackable) when triggered.
/obj/item/circuit_component/mod_program/radar/input_received(datum/port/port)
var/datum/computer_file/program/radar/radar = associated_program
var/atom/radar_atom = radar.find_atom()
if(target.value != radar_atom)
radar.selected = REF(target.value)
SStgui.update_uis(radar.computer)
if(radar.trackable(radar_atom))
var/turf/turf = get_turf(radar_atom)
x_pos.set_value(turf.x)
y_pos.set_value(turf.y)
else
x_pos.set_value(null)
y_pos.set_value(null)
/**
* Check if we can track the object. When making different definitions of this proc for subtypes, include typical
* targets as an exception to this (e.g humans for lifeline) so that even if they're coming from a circuit input
* they won't get filtered by the maximum distance, because they're "supported entities".
*/
/obj/item/circuit_component/mod_program/radar/proc/can_track(datum/source, atom/signal, signal_turf, computer_turf)
SIGNAL_HANDLER
if(target.value && get_dist_euclidian(computer_turf, signal_turf) > MAX_RADAR_CIRCUIT_DISTANCE)
return COMPONENT_RADAR_DONT_TRACK
return COMPONENT_RADAR_TRACK_ANYWAY
///Return the value of the target port.
/obj/item/circuit_component/mod_program/radar/proc/get_atom(datum/source, list/atom_container)
SIGNAL_HANDLER
atom_container[1] = target.value
/**
* When a target is selected by the app, reset the target port, update the x/pos ports (if trackable)
* and set selected_by_app port to the target atom.
*/
/obj/item/circuit_component/mod_program/radar/proc/on_selected(datum/source, selected_ref)
SIGNAL_HANDLER
target.set_value(null)
var/datum/computer_file/program/radar/radar = associated_program
var/atom/selected_atom = radar.find_atom()
selected_by_app.set_value(selected_atom)
if(radar.trackable(selected_atom))
var/turf/turf = get_turf(radar.selected)
x_pos.set_value(turf.x)
y_pos.set_value(turf.y)
else
x_pos.set_value(null)
y_pos.set_value(null)
/obj/item/circuit_component/mod_program/radar/medical
associated_program = /datum/computer_file/program/radar/lifeline
/obj/item/circuit_component/mod_program/radar/medical/can_track(datum/source, atom/signal, signal_turf, computer_turf)
if(target.value in GLOB.human_list)
return NONE
return ..()
/obj/item/circuit_component/mod_program/radar/janitor
associated_program = /datum/computer_file/program/radar/custodial_locator
/obj/item/circuit_component/mod_program/radar/janitor/can_track(datum/source, atom/signal, signal_turf, computer_turf)
if(target.value in GLOB.janitor_devices)
return NONE
return ..()
/obj/item/circuit_component/mod_program/radar/nukie
associated_program = /datum/computer_file/program/radar/fission360
/obj/item/circuit_component/mod_program/radar/nukie/can_track(datum/source, atom/signal, signal_turf, computer_turf)
if(target.value in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/nuclearbomb))
return NONE
if(target.value in SSpoints_of_interest.real_nuclear_disks)
return NONE
if(target.value == SSshuttle.getShuttle("syndicate"))
return NONE
return ..()
#undef MAX_RADAR_CIRCUIT_DISTANCE
#undef RADAR_NOT_TRACKABLE
#undef RADAR_TRACKABLE
#undef RADAR_TRACKABLE_ANYWAY
@@ -83,7 +83,8 @@
return data
/datum/computer_file/program/robocontrol/ui_act(action, list/params, datum/tgui/ui)
/datum/computer_file/program/robocontrol/ui_act(action, list/params, datum/tgui/ui, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/current_user = ui.user
var/obj/item/card/id/id_card = computer?.computer_id_slot
@@ -84,6 +84,7 @@
return data
/datum/computer_file/program/robotact/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
//Implied type, memes
var/obj/item/modular_computer/pda/silicon/tablet = computer
var/mob/living/silicon/robot/cyborg = tablet.silicon_owner
@@ -8,6 +8,8 @@
tgui_id = "NtosSignaler"
program_icon = "satellite-dish"
can_run_on_flags = PROGRAM_PDA | PROGRAM_LAPTOP
program_flags = /datum/computer_file/program::program_flags | PROGRAM_CIRCUITS_RUN_WHEN_CLOSED
circuit_comp_type = /obj/item/circuit_component/mod_program/signaler
///What is the saved signal frequency?
var/signal_frequency = FREQ_SIGNALER
/// What is the saved signal code?
@@ -36,10 +38,11 @@
data["maxFrequency"] = MAX_FREE_FREQ
return data
/datum/computer_file/program/signal_commander/ui_act(action, list/params)
/datum/computer_file/program/signal_commander/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("signal")
INVOKE_ASYNC(src, PROC_REF(signal))
INVOKE_ASYNC(src, PROC_REF(signal), usr)
. = TRUE
if("freq")
var/new_signal_frequency = sanitize_frequency(unformat_frequency(params["freq"]), TRUE)
@@ -56,27 +59,65 @@
signal_code = initial(signal_code)
. = TRUE
/datum/computer_file/program/signal_commander/proc/signal()
/datum/computer_file/program/signal_commander/proc/signal(atom/source)
if(!radio_connection)
return
var/mob/user
var/obj/item/circuit_component/signaling
if(ismob(source))
user = source
else if(istype(source, /obj/item/circuit_component))
signaling = source
if(!COOLDOWN_FINISHED(src, signal_cooldown))
computer.balloon_alert(usr, "cooling down!")
if(user)
computer.balloon_alert(user, "cooling down!")
return
COOLDOWN_START(src, signal_cooldown, signal_cooldown_time)
computer.balloon_alert(usr, "signaled")
if(user)
computer.balloon_alert(user, "signaled")
var/time = time2text(world.realtime,"hh:mm:ss")
var/turf/T = get_turf(computer)
var/logging_data = "[time] <B>:</B> [key_name(usr)] used the computer '[initial(computer.name)]' @ location ([T.x],[T.y],[T.z]) <B>:</B> [format_frequency(signal_frequency)]/[signal_code]"
var/user_deets
if(signaling)
user_deets = "[signaling.parent.get_creator()]"
else
user_deets = "[key_name(usr)]"
var/logging_data = "[time] <B>:</B> [user_deets] used the computer '[initial(computer.name)]' @ location ([T.x],[T.y],[T.z]) <B>:</B> [format_frequency(signal_frequency)]/[signal_code]"
add_to_signaler_investigate_log(logging_data)
var/datum/signal/signal = new(list("code" = signal_code), logging_data = logging_data)
var/datum/signal/signal = new(list("code" = signal_code, "key" = signaling?.parent.owner_id), logging_data = logging_data)
radio_connection.post_signal(computer, signal)
/datum/computer_file/program/signal_commander/proc/set_frequency(new_frequency)
SSradio.remove_object(computer, signal_frequency)
signal_frequency = new_frequency
radio_connection = SSradio.add_object(computer, signal_frequency, RADIO_SIGNALER)
/obj/item/circuit_component/mod_program/signaler
associated_program = /datum/computer_file/program/signal_commander
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
/// Frequency input
var/datum/port/input/freq
/// Signal input
var/datum/port/input/code
/obj/item/circuit_component/mod_program/signaler/populate_ports()
. = ..()
freq = add_input_port("Frequency", PORT_TYPE_NUMBER, trigger = PROC_REF(set_freq), default = FREQ_SIGNALER)
code = add_input_port("Code", PORT_TYPE_NUMBER, trigger = PROC_REF(set_code), default = DEFAULT_SIGNALER_CODE)
/obj/item/circuit_component/mod_program/signaler/proc/set_freq(datum/port/port)
var/datum/computer_file/program/signal_commander/signaler = associated_program
signaler.set_frequency(clamp(freq.value, MIN_FREE_FREQ, MAX_FREE_FREQ))
/obj/item/circuit_component/mod_program/signaler/proc/set_code(datum/port/port)
var/datum/computer_file/program/signal_commander/signaler = associated_program
signaler.signal_code = round(clamp(code.value, 1, 100))
/obj/item/circuit_component/mod_program/signaler/input_received(datum/port/port)
INVOKE_ASYNC(associated_program, TYPE_PROC_REF(/datum/computer_file/program/signal_commander, signal), src)
@@ -50,7 +50,8 @@
return null
/datum/computer_file/program/skill_tracker/ui_act(action, params, datum/tgui/ui)
/datum/computer_file/program/skill_tracker/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_reward")
var/skill_type = find_skilltype(params["skill"])
@@ -55,6 +55,7 @@
return data
/datum/computer_file/program/supermatter_monitor/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_refresh")
refresh()
@@ -4,6 +4,7 @@
program_icon = "signal"
program_open_overlay = "generic"
size = 1
circuit_comp_type = /obj/item/circuit_component/mod_program/status
extended_desc = "An app used to change the message on the station status displays."
tgui_id = "NtosStatus"
@@ -42,16 +43,16 @@
* * upper - Top text
* * lower - Bottom text
*/
/datum/computer_file/program/status/proc/post_message(upper, lower)
/datum/computer_file/program/status/proc/post_message(upper, lower, log_usr = key_name(usr))
post_status("message", upper, lower)
log_game("[key_name(usr)] has changed the station status display message to \"[upper] [lower]\" [loc_name(usr)]")
log_game("[log_usr] has changed the station status display message to \"[upper] [lower]\" [loc_name(usr)]")
/**
* Post a picture to status displays
* Arguments:
* * picture - The picture name
*/
/datum/computer_file/program/status/proc/post_picture(picture)
/datum/computer_file/program/status/proc/post_picture(picture, log_usr = key_name(usr))
if (!(picture in GLOB.status_display_approved_pictures))
return
if(picture in GLOB.status_display_state_pictures)
@@ -80,9 +81,10 @@
else
post_status("alert", picture)
log_game("[key_name(usr)] has changed the station status display message to \"[picture]\" [loc_name(usr)]")
log_game("[log_usr] has changed the station status display message to \"[picture]\" [loc_name(usr)]")
/datum/computer_file/program/status/ui_act(action, list/params, datum/tgui/ui)
/datum/computer_file/program/status/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("setStatusMessage")
upper_text = reject_bad_text(params["upperText"] || "", MAX_STATUS_LINE_LENGTH)
@@ -104,3 +106,26 @@
data["lowerText"] = lower_text
return data
/obj/item/circuit_component/mod_program/status
associated_program = /datum/computer_file/program/status
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
///When the trigger is signaled, this will be the upper text of status displays.
var/datum/port/input/upper_text
///When the trigger is signaled, this will be the bottom text.
var/datum/port/input/bottom_text
///A list port that, when signaled, will set the status image to one of its values
var/datum/port/input/status_display_pics
/obj/item/circuit_component/mod_program/status/populate_options()
status_display_pics = add_option_port("Set Status Display Picture", GLOB.status_display_approved_pictures, trigger = PROC_REF(set_picture))
/obj/item/circuit_component/mod_program/status/proc/set_picture(datum/port/port)
var/datum/computer_file/program/status/status = associated_program
INVOKE_ASYNC(status, TYPE_PROC_REF(/datum/computer_file/program/status, post_picture), status_display_pics.value, parent.get_creator())
/obj/item/circuit_component/mod_program/status/input_received(datum/port/port)
var/datum/computer_file/program/status/status = associated_program
INVOKE_ASYNC(status, TYPE_PROC_REF(/datum/computer_file/program/status, post_message), upper_text.value, bottom_text.value, parent.get_creator())
@@ -88,7 +88,8 @@
)
return data
/datum/computer_file/program/science/ui_act(action, list/params)
/datum/computer_file/program/science/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
// Check if the console is locked to block any actions occuring
if (locked && action != "toggleLock")
computer.say("Console is locked, cannot perform further actions.")
@@ -23,6 +23,7 @@
return data
/datum/computer_file/program/themeify/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("PRG_change_theme")
var/selected_theme = params["selected_theme"]
@@ -9,8 +9,10 @@
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
tgui_id = "NtosNetMonitor"
program_icon = "network-wired"
circuit_comp_type = /obj/item/circuit_component/mod_program/ntnetmonitor
/datum/computer_file/program/ntnetmonitor/ui_act(action, list/params, datum/tgui/ui)
/datum/computer_file/program/ntnetmonitor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
switch(action)
if("resetIDS")
SSmodular_computers.intrusion_detection_alarm = FALSE
@@ -66,3 +68,64 @@
data["tablets"] += list(tablet_data)
return data
/obj/item/circuit_component/mod_program/ntnetmonitor
associated_program = /datum/computer_file/program/ntnetmonitor
///The stored NTnet relay or PDA to be used as the target of triggers
var/datum/port/input/target
///Sets `intrusion_detection_alarm` when triggered
var/datum/port/input/toggle_ids
///Toggles the target ntnet relay on/off when triggered
var/datum/port/input/toggle_relay
///Purges modpc logs when triggered
var/datum/port/input/purge_logs
///Toggles the spam mode of the target PDA when triggered
var/datum/port/input/toggle_mass_pda
///Toggle mime mode of the target PDA when triggered
var/datum/port/input/toggle_mime_mode
///Returns a list of all PDA Messengers when the "Get Messengers" input is pinged
var/datum/port/output/all_messengers
///See above
var/datum/port/input/get_pdas
/obj/item/circuit_component/mod_program/ntnetmonitor/populate_ports()
. = ..()
target = add_input_port("Target Messenger/Relay", PORT_TYPE_DATUM, order = 0.5)
toggle_ids = add_input_port("Toggle IDS Status", PORT_TYPE_SIGNAL, trigger = PROC_REF(toggle_ids))
toggle_relay = add_input_port("Toggle NTnet Relay", PORT_TYPE_SIGNAL, trigger = PROC_REF(toggle_relay))
purge_logs = add_input_port("Purge Logs", PORT_TYPE_SIGNAL, trigger = PROC_REF(purge_logs))
toggle_mass_pda = add_input_port("Toggle Mass Messenger", PORT_TYPE_SIGNAL, trigger = PROC_REF(toggle_pda_stuff))
toggle_mime_mode = add_input_port("Toggle Mime Mode", PORT_TYPE_SIGNAL, trigger = PROC_REF(toggle_pda_stuff))
get_pdas = add_input_port("Get PDAs", PORT_TYPE_SIGNAL, trigger = PROC_REF(get_pdas))
all_messengers = add_output_port("List of PDAs", PORT_TYPE_LIST(PORT_TYPE_ATOM))
/obj/item/circuit_component/mod_program/ntnetmonitor/proc/get_pdas(datum/port/port)
var/list/computers_with_messenger = list()
for(var/messenger_ref as anything in GLOB.pda_messengers)
var/datum/computer_file/program/messenger/messenger = GLOB.pda_messengers[messenger_ref]
computers_with_messenger += messenger.computer
all_messengers.set_value(computers_with_messenger)
/obj/item/circuit_component/mod_program/ntnetmonitor/proc/toggle_ids(datum/port/port)
SSmodular_computers.intrusion_detection_enabled = !SSmodular_computers.intrusion_detection_enabled
/obj/item/circuit_component/mod_program/ntnetmonitor/proc/toggle_relay(datum/port/port)
var/obj/machinery/ntnet_relay/target_relay = target.value
if(!istype(target_relay))
return
target_relay.set_relay_enabled(!target_relay.relay_enabled)
/obj/item/circuit_component/mod_program/ntnetmonitor/proc/purge_logs(datum/port/port)
SSmodular_computers.purge_logs()
/obj/item/circuit_component/mod_program/ntnetmonitor/proc/toggle_pda_stuff(datum/port/port)
var/obj/item/modular_computer/computer = target.value
if(!istype(computer))
return
var/datum/computer_file/program/messenger/target_messenger = locate() in computer.stored_files
if(isnull(target_messenger))
return
if(COMPONENT_TRIGGERED_BY(toggle_mass_pda, port))
target_messenger.spam_mode = !target_messenger.spam_mode
if(COMPONENT_TRIGGERED_BY(toggle_mime_mode, port))
target_messenger.mime_mode = !target_messenger.mime_mode