diff --git a/code/modules/wiremod/components/action/speech.dm b/code/modules/wiremod/components/action/speech.dm
index f149cba9122..32495889faf 100644
--- a/code/modules/wiremod/components/action/speech.dm
+++ b/code/modules/wiremod/components/action/speech.dm
@@ -9,6 +9,8 @@
category = "Action"
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
+ /// Spans to apply to the output message.
+ var/datum/port/input/color_input // BUBBER ADDITION
/// The message to send
var/datum/port/input/message
/// The quiet mode flag
@@ -22,6 +24,7 @@
. += create_ui_notice("Speech Cooldown: [DisplayTimeText(speech_cooldown)]", "orange", "stopwatch")
/obj/item/circuit_component/speech/populate_ports()
+ color_input = add_input_port("Color", PORT_TYPE_STRING, default = "Default") // BUBBER ADDITION
message = add_input_port("Message", PORT_TYPE_STRING, trigger = null)
quietmode = add_input_port("Quiet Mode", PORT_TYPE_NUMBER, default = 0)
@@ -32,7 +35,12 @@
if(TIMER_COOLDOWN_RUNNING(parent.shell, COOLDOWN_CIRCUIT_SPEECH))
return
+ // BUBBER ADDITION
+ if(!(color_input.value in GLOB.component_span_color_list))
+ color_input.set_value("Default", TRUE)
+ // BUBBER ADDITION END
+
if(message.value)
var/atom/movable/shell = parent.shell
- shell.say(message.value, forced = "circuit speech | [parent.get_creator()]", message_range = quietmode.value > 0 ? WHISPER_RANGE : MESSAGE_RANGE)
+ shell.say(message.value, forced = "circuit speech | [parent.get_creator()]", message_range = quietmode.value > 0 ? WHISPER_RANGE : MESSAGE_RANGE, spans = list(GLOB.component_span_color_list[color_input.value])) // BUBBER EDIT
TIMER_COOLDOWN_START(shell, COOLDOWN_CIRCUIT_SPEECH, speech_cooldown)
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index 72d6ca75180..ffb7dd5beaa 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -87,6 +87,7 @@
/// A reference to the action button to look at charge/get info
var/datum/action/innate/bci_charge_action/charge_action
+ var/datum/port/input/color_input // BUBBER ADDITION
var/datum/port/input/message
var/datum/port/input/send_message_signal
var/datum/port/input/show_charge_meter
@@ -96,7 +97,7 @@
var/obj/item/organ/cyberimp/bci/bci
/obj/item/circuit_component/bci_core/populate_ports()
-
+ color_input = add_input_port("Color", PORT_TYPE_STRING, default = "Default") // BUBBER ADDITION
message = add_input_port("Message", PORT_TYPE_STRING, trigger = null)
send_message_signal = add_input_port("Send Message", PORT_TYPE_SIGNAL)
show_charge_meter = add_input_port("Show Charge Meter", PORT_TYPE_NUMBER, trigger = PROC_REF(update_charge_action))
@@ -154,13 +155,16 @@
if (!sent_message)
return
+ if(!(color_input.value in GLOB.component_span_color_list)) // BUBBER ADDITION
+ color_input.set_value("Default")
+
if (isnull(bci.owner))
return
if (bci.owner.stat == DEAD)
return
- to_chat(bci.owner, "You hear a strange, robotic voice in your head... \"[span_robot("[html_encode(sent_message)]")]\"")
+ to_chat(bci.owner, "You hear a strange, robotic voice in your head... \"[span_robot("[html_encode(sent_message)]")]\"") // BUBBER EDIT
/obj/item/circuit_component/bci_core/proc/on_organ_implanted(datum/source, mob/living/carbon/owner)
SIGNAL_HANDLER
diff --git a/modular_zubbers/code/__DEFINES/span.dm b/modular_zubbers/code/__DEFINES/span.dm
index 951d3fe78f0..fe76cb37933 100644
--- a/modular_zubbers/code/__DEFINES/span.dm
+++ b/modular_zubbers/code/__DEFINES/span.dm
@@ -1 +1,15 @@
#define span_center(str) ("[str]")
+
+/// A key:value pair list for spans, used in circuitry to determine the color of a message and to allow Var Editing by admins. Safest way to implement while preventing people from inputting their own spans in circuits.
+GLOBAL_LIST_INIT(component_span_color_list, list(
+ "Default" = "span_hear",
+ "Green" = "radio",
+ "Purple" = "sciradio",
+ "Yellow" = "comradio",
+ "Red" = "secradio",
+ "Blue" = "medradio",
+ "Orange" = "engradio",
+ "Brown" = "suppradio",
+ "Maroon" = "syndradio",
+ "Pink" = "aiprivradio"
+))
diff --git a/modular_zubbers/code/modules/research/designs/wiremod_designs.dm b/modular_zubbers/code/modules/research/designs/wiremod_designs.dm
new file mode 100644
index 00000000000..80e466b92ec
--- /dev/null
+++ b/modular_zubbers/code/modules/research/designs/wiremod_designs.dm
@@ -0,0 +1,4 @@
+/datum/design/component/color
+ name = "Color Component"
+ id = "comp_color"
+ build_path = /obj/item/circuit_component/color
diff --git a/modular_zubbers/code/modules/research/techweb/circuit_nodes.dm b/modular_zubbers/code/modules/research/techweb/circuit_nodes.dm
new file mode 100644
index 00000000000..f9842bc6776
--- /dev/null
+++ b/modular_zubbers/code/modules/research/techweb/circuit_nodes.dm
@@ -0,0 +1,93 @@
+/datum/techweb_node/programming
+ id = TECHWEB_NODE_PROGRAMMING
+ starting_node = TRUE
+ display_name = "Programming"
+ description = "Dedicate an entire shift to program a fridge to greet you when opened."
+ prereq_ids = list(TECHWEB_NODE_ROBOTICS)
+ design_ids = list(
+ "component_printer",
+ "module_duplicator",
+ "circuit_multitool",
+ "compact_remote_shell",
+ "usb_cable",
+ "integrated_circuit",
+ "comp_access_checker",
+ "comp_arctan2",
+ "comp_arithmetic",
+ "comp_assoc_list_pick",
+ "comp_assoc_list_remove",
+ "comp_assoc_list_set",
+ "comp_binary_convert",
+ "comp_clock",
+ "comp_comparison",
+ "comp_concat",
+ "comp_concat_list",
+ "comp_decimal_convert",
+ "comp_delay",
+ "comp_direction",
+ "comp_element_find",
+ "comp_filter_list",
+ "comp_foreach",
+ "comp_format",
+ "comp_format_assoc",
+ "comp_get_column",
+ "comp_gps",
+ "comp_health",
+ "comp_health_state",
+ "comp_hear",
+ "comp_id_access_reader",
+ "comp_id_getter",
+ "comp_id_info_reader",
+ "comp_index",
+ "comp_index_assoc",
+ "comp_index_table",
+ "comp_laserpointer",
+ "comp_length",
+ "comp_light",
+ "comp_list_add",
+ "comp_list_assoc_literal",
+ "comp_list_clear",
+ "comp_list_literal",
+ "comp_list_pick",
+ "comp_list_remove",
+ "comp_logic",
+ "comp_matscanner",
+ "comp_mmi",
+ "comp_module",
+ "comp_multiplexer",
+ "comp_not",
+ "comp_ntnet_receive",
+ "comp_ntnet_send",
+ "comp_ntnet_send_list_literal",
+ "comp_nfc_send",
+ "comp_nfc_receive",
+ "comp_pinpointer",
+ "comp_pressuresensor",
+ "comp_radio",
+ "comp_random",
+ "comp_reagents",
+ "comp_router",
+ "comp_select_query",
+ "comp_self",
+ "comp_set_variable_trigger",
+ "comp_soundemitter",
+ "comp_species",
+ "comp_speech",
+ "comp_split",
+ "comp_string_contains",
+ "comp_tempsensor",
+ "comp_textcase",
+ "comp_timepiece",
+ "comp_toggle",
+ "comp_tonumber",
+ "comp_tostring",
+ "comp_color",
+ "comp_trigonometry",
+ "comp_typecast",
+ "comp_typecheck",
+ "comp_view_sensor",
+ "comp_wire_bundle",
+ "comp_wirenet_receive",
+ "comp_wirenet_send",
+ "comp_wirenet_send_literal",
+ )
diff --git a/modular_zubbers/code/modules/wiremod/components/string/color.dm b/modular_zubbers/code/modules/wiremod/components/string/color.dm
new file mode 100644
index 00000000000..52b43d64de0
--- /dev/null
+++ b/modular_zubbers/code/modules/wiremod/components/string/color.dm
@@ -0,0 +1,30 @@
+/obj/item/circuit_component/color
+ display_name = "Color"
+ desc = "A component that has a drop-down of pickable colors to change the color in chat."
+ category = "String"
+
+ /// Colors to pick from to change to, list defined in spans.dm (modular_zubber)
+ var/datum/port/input/option/color_options
+ /// Whether the color is on or not
+ var/datum/port/input/on
+ /// The result of the text operation
+ var/datum/port/output/output
+
+ circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
+
+/obj/item/circuit_component/color/populate_options()
+ var/static/component_options = GLOB.component_span_color_list
+ color_options = add_option_port("Color Options", component_options)
+
+/obj/item/circuit_component/color/populate_ports()
+ on = add_input_port("On", PORT_TYPE_NUMBER)
+ output = add_output_port("Output", PORT_TYPE_STRING)
+
+/obj/item/circuit_component/color/input_received(datum/port/input/port)
+ if(!on.value)
+ return
+
+ if(isnull(color_options.value))
+ return
+
+ output.set_output(color_options.value)
diff --git a/tgstation.dme b/tgstation.dme
index f64a2d3dad4..80677bc1b50 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -9548,7 +9548,9 @@
#include "modular_zubbers\code\modules\research\designs\medical_designs.dm"
#include "modular_zubbers\code\modules\research\designs\misc_designs.dm"
#include "modular_zubbers\code\modules\research\designs\nerd_designs.dm"
+#include "modular_zubbers\code\modules\research\designs\wiremod_designs.dm"
#include "modular_zubbers\code\modules\research\techweb\all_nodes.dm"
+#include "modular_zubbers\code\modules\research\techweb\circuit_nodes.dm"
#include "modular_zubbers\code\modules\rr_opt_in\examine.dm"
#include "modular_zubbers\code\modules\rr_opt_in\rr_opt_config.dm"
#include "modular_zubbers\code\modules\rr_opt_in\rr_optin_preferences.dm"
@@ -9678,6 +9680,7 @@
#include "modular_zubbers\code\modules\voting\_votes.dm"
#include "modular_zubbers\code\modules\voting\living_timer.dm"
#include "modular_zubbers\code\modules\voting\vote_overrides.dm"
+#include "modular_zubbers\code\modules\wiremod\components\string\color.dm"
#include "modular_zubbers\code\modules\wizard_dize\wizard_dice_event.dm"
#include "modular_zubbers\code\modules\wizard_dize\wizard_dice_object.dm"
#include "modular_zubbers\code\modules\zantag_tickets\_defines.dm"