mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-16 02:24:11 +01:00
[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:
@@ -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
|
||||
+60
-30
@@ -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>"
|
||||
|
||||
Reference in New Issue
Block a user