From 53518e043bfe5c8bef980b3e5230e3933fb62b94 Mon Sep 17 00:00:00 2001 From: tmyqlfpir <80724828+tmyqlfpir@users.noreply.github.com> Date: Thu, 16 May 2024 05:31:18 +1000 Subject: [PATCH] Circuit editor QoL and new circuit components (#82969) ## About The Pull Request This PR introduces a number of minor quality of life improvements to already existing circuit components, and adds three new components. preview ## Why It's Good For The Game This improves the overall experience for circuits. ### UI changes grid aligned Grid align is now an option for circuit designers. It will round objects to the nearest 10px units internally, and can be toggled on/off by the new additional button beside the component menu button. This makes circuits easier on the eyes as things are pixel perfect aligned. tooltips All three buttons have been given tool tips. ### Tweaked Components voice activator * Adds a on/off flag to the voice activator component -- This saves power for circuits as you're not forced to use a compare flag check to turn off voice activation speech * Adds a quiet mode flag to speech component -- This is ideal when you want a device to speak, but don't want other people to hear. A good example would be a handheld translator that you only want to hear yourself. ### New Components ntnet list literal * An NTNet Send component that allows everything to be input much like the list literal component -- This makes sending stuff over NTNet easier for the user, and use less power as it won't require an additional list literal component compare health state * A health comparison component that checks the entity's health state, and can return true or false depending if the entity is alive, sleeping, unconscious, critical or dead -- This could be achieved by using a health sensor and a compare component, however it lacks the ability to know when a entity is unconscious or sleeping toggle * A quick toggle component to allow the switching between a true and false state -- This could be achieved by using a logic component and self linking, however this makes it far easier for newcomers to make something as simple as an on/off switch (such as a handheld translator which uses the front button to turn on/off) ## Changelog :cl: qol: Add tooltips to circuit editor buttons qol: Add grid alignment mode to circuit editor add: Added new compare health state component add: Added new NTNet send list literal component add: Added new toggle component qol: Added activity toggle to voice activator component qol: Added quiet mode to speech component qol: NTNet send component will not use power/trigger if NTNet is offline /:cl: --- .../research/designs/wiremod_designs.dm | 15 +++++ code/modules/research/techweb/all_nodes.dm | 3 + .../wiremod/components/action/speech.dm | 5 +- .../wiremod/components/atom/health_state.dm | 56 +++++++++++++++++++ code/modules/wiremod/components/atom/hear.dm | 6 ++ .../modules/wiremod/components/math/toggle.dm | 36 ++++++++++++ .../wiremod/components/ntnet/ntnet_send.dm | 11 +++- .../components/ntnet/ntnet_send_literal.dm | 30 ++++++++++ code/modules/wiremod/core/component.dm | 11 ++++ .../wiremod/core/integrated_circuit.dm | 11 ++++ tgstation.dme | 3 + .../IntegratedCircuit/ObjectComponent.jsx | 15 +++-- .../interfaces/IntegratedCircuit/index.jsx | 15 +++++ 13 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 code/modules/wiremod/components/atom/health_state.dm create mode 100644 code/modules/wiremod/components/math/toggle.dm create mode 100644 code/modules/wiremod/components/ntnet/ntnet_send_literal.dm diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm index 3606dd67e2e..204ecaa289b 100644 --- a/code/modules/research/designs/wiremod_designs.dm +++ b/code/modules/research/designs/wiremod_designs.dm @@ -81,6 +81,11 @@ id = "comp_logic" build_path = /obj/item/circuit_component/compare/logic +/datum/design/component/toggle + name = "Toggle Component" + id = "comp_toggle" + build_path = /obj/item/circuit_component/compare/toggle + /datum/design/component/delay name = "Delay Component" id = "comp_delay" @@ -221,6 +226,11 @@ id = "comp_health" build_path = /obj/item/circuit_component/health +/datum/design/component/compare/health_state + name = "Compare Health State Component" + id = "comp_health_state" + build_path = /obj/item/circuit_component/compare/health_state + /datum/design/component/matscanner name = "Material Scanner" id = "comp_matscanner" @@ -337,6 +347,11 @@ id = "comp_ntnet_send" build_path = /obj/item/circuit_component/ntnet_send +/datum/design/component/list_literal/ntnet_send + name = "NTNet Transmitter List Literal" + id = "comp_ntnet_send_list_literal" + build_path = /obj/item/circuit_component/list_literal/ntnet_send + /datum/design/component/list_literal name = "List Literal Component" id = "comp_list_literal" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 37245958aa3..69552a4923c 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -344,6 +344,7 @@ "comp_get_column", "comp_gps", "comp_health", + "comp_health_state", "comp_hear", "comp_id_access_reader", "comp_id_getter", @@ -368,6 +369,7 @@ "comp_not", "comp_ntnet_receive", "comp_ntnet_send", + "comp_ntnet_send_list_literal", "comp_pinpointer", "comp_pressuresensor", "comp_radio", @@ -386,6 +388,7 @@ "comp_tempsensor", "comp_textcase", "comp_timepiece", + "comp_toggle", "comp_tonumber", "comp_tostring", "comp_trigonometry", diff --git a/code/modules/wiremod/components/action/speech.dm b/code/modules/wiremod/components/action/speech.dm index 0e2936bcfbf..f149cba9122 100644 --- a/code/modules/wiremod/components/action/speech.dm +++ b/code/modules/wiremod/components/action/speech.dm @@ -11,6 +11,8 @@ /// The message to send var/datum/port/input/message + /// The quiet mode flag + var/datum/port/input/quietmode /// The cooldown for this component of how often it can send speech messages. var/speech_cooldown = 1 SECONDS @@ -21,6 +23,7 @@ /obj/item/circuit_component/speech/populate_ports() message = add_input_port("Message", PORT_TYPE_STRING, trigger = null) + quietmode = add_input_port("Quiet Mode", PORT_TYPE_NUMBER, default = 0) /obj/item/circuit_component/speech/input_received(datum/port/input/port) if(!parent.shell) @@ -31,5 +34,5 @@ if(message.value) var/atom/movable/shell = parent.shell - shell.say(message.value, forced = "circuit speech | [parent.get_creator()]") + shell.say(message.value, forced = "circuit speech | [parent.get_creator()]", message_range = quietmode.value > 0 ? WHISPER_RANGE : MESSAGE_RANGE) TIMER_COOLDOWN_START(shell, COOLDOWN_CIRCUIT_SPEECH, speech_cooldown) diff --git a/code/modules/wiremod/components/atom/health_state.dm b/code/modules/wiremod/components/atom/health_state.dm new file mode 100644 index 00000000000..dc83a41fdfd --- /dev/null +++ b/code/modules/wiremod/components/atom/health_state.dm @@ -0,0 +1,56 @@ +/** + * # Compare Health State Component + * + * Returns true when state matches entity. + */ + +/obj/item/circuit_component/compare/health_state + display_name = "Compare Health State" + desc = "A component that compares the health state of an organism, and returns true or false." + category = "Entity" + + /// The input port + var/datum/port/input/input_port + + /// Compare state option + var/datum/port/input/option/state_option + + var/max_range = 5 + +/obj/item/circuit_component/compare/health_state/get_ui_notices() + . = ..() + . += create_ui_notice("Maximum Range: [max_range] tiles", "orange", "info") + +/obj/item/circuit_component/compare/health_state/populate_options() + input_port = add_input_port("Organism", PORT_TYPE_ATOM) + + var/static/component_options = list( + "Alive", + "Asleep", + "Critical", + "Unconscious", + "Deceased", + ) + state_option = add_option_port("Comparison Option", component_options) + +/obj/item/circuit_component/compare/health_state/do_comparisons() + var/mob/living/organism = input_port.value + var/turf/current_turf = get_location() + if(!istype(organism) || current_turf.z != organism.z || get_dist(current_turf, organism) > max_range) + return FALSE + + var/current_option = state_option.value + var/state = organism.stat + switch(current_option) + if("Alive") + return state != DEAD + if("Asleep") + return !!organism.IsSleeping() && !organism.IsUnconscious() + if("Critical") + return state == SOFT_CRIT || state == HARD_CRIT + if("Unconscious") + return state == UNCONSCIOUS || state == HARD_CRIT || !!organism.IsUnconscious() + if("Deceased") + return state == DEAD + //Unknown state, something fucked up really bad - just return false + return FALSE diff --git a/code/modules/wiremod/components/atom/hear.dm b/code/modules/wiremod/components/atom/hear.dm index 3c3f05691b2..98c45a77f6f 100644 --- a/code/modules/wiremod/components/atom/hear.dm +++ b/code/modules/wiremod/components/atom/hear.dm @@ -8,6 +8,9 @@ desc = "A component that listens for messages. Requires a shell." category = "Entity" + /// The on/off port + var/datum/port/input/on + /// The message heard var/datum/port/output/message_port /// The language heard @@ -20,6 +23,7 @@ var/datum/port/output/trigger_port /obj/item/circuit_component/hear/populate_ports() + on = add_input_port("On", PORT_TYPE_NUMBER, default = 1) message_port = add_output_port("Message", PORT_TYPE_STRING) language_port = add_output_port("Language", PORT_TYPE_STRING) speaker_port = add_output_port("Speaker", PORT_TYPE_ATOM) @@ -40,6 +44,8 @@ return Hear(arglist(arguments)) /obj/item/circuit_component/hear/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) + if(!on.value) + return FALSE if(speaker == parent?.shell) return FALSE diff --git a/code/modules/wiremod/components/math/toggle.dm b/code/modules/wiremod/components/math/toggle.dm new file mode 100644 index 00000000000..9f51c974cb3 --- /dev/null +++ b/code/modules/wiremod/components/math/toggle.dm @@ -0,0 +1,36 @@ +/** + * # Toggle Component + * + * Does a toggle between true and false on trigger + */ +/obj/item/circuit_component/compare/toggle + display_name = "Toggle" + desc = "A component that toggles between on and off when triggered. All input ports (except for set toggle) will trigger the component." + category = "Math" + + /// A signal to reset the toggle back to 0 + var/datum/port/input/toggle_set + /// A signal to toggle and return the current state + var/datum/port/input/toggle_and_compare + + var/toggle_state = FALSE + +/obj/item/circuit_component/compare/toggle/populate_custom_ports() + toggle_set = add_input_port("Set Toggle State", PORT_TYPE_NUMBER) + toggle_and_compare = add_input_port("Toggle And Compare", PORT_TYPE_SIGNAL) + toggle_state = FALSE + +/obj/item/circuit_component/compare/toggle/input_received(datum/port/input/port) + if(port == toggle_set) + toggle_state = !!port.value + return + if(COMPONENT_TRIGGERED_BY(toggle_and_compare, port)) + toggle_state = !toggle_state + if(toggle_state) + true.set_output(COMPONENT_SIGNAL) + else + false.set_output(COMPONENT_SIGNAL) + return ..() + +/obj/item/circuit_component/compare/toggle/do_comparisons() + return toggle_state diff --git a/code/modules/wiremod/components/ntnet/ntnet_send.dm b/code/modules/wiremod/components/ntnet/ntnet_send.dm index 105af11760b..7ff4372927f 100644 --- a/code/modules/wiremod/components/ntnet/ntnet_send.dm +++ b/code/modules/wiremod/components/ntnet/ntnet_send.dm @@ -27,12 +27,17 @@ data_package = add_input_port("Data Package", PORT_TYPE_LIST(PORT_TYPE_ANY)) enc_key = add_input_port("Encryption Key", PORT_TYPE_STRING) +/obj/item/circuit_component/ntnet_send/should_receive_input(datum/port/input/port) + . = ..() + if(!.) + return FALSE + /// If the server is down, don't use power or attempt to send data + return find_functional_ntnet_relay() + /obj/item/circuit_component/ntnet_send/pre_input_received(datum/port/input/port) if(port == list_options) var/new_datatype = list_options.value data_package.set_datatype(PORT_TYPE_LIST(new_datatype)) /obj/item/circuit_component/ntnet_send/input_received(datum/port/input/port) - if(!find_functional_ntnet_relay()) - return - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CIRCUIT_NTNET_DATA_SENT, list("data" = data_package.value, "enc_key" = enc_key.value, "port" = WEAKREF(data_package))) + send_ntnet_data(data_package, enc_key.value) diff --git a/code/modules/wiremod/components/ntnet/ntnet_send_literal.dm b/code/modules/wiremod/components/ntnet/ntnet_send_literal.dm new file mode 100644 index 00000000000..49e1c2b00ab --- /dev/null +++ b/code/modules/wiremod/components/ntnet/ntnet_send_literal.dm @@ -0,0 +1,30 @@ +/** + * # NTNet Transmitter List Literal Component + * + * Create a list literal and send a data package through NTNet + * + * This file is based off of ntnet_send.dm + * Any changes made to those files should be copied over with discretion + */ +/obj/item/circuit_component/list_literal/ntnet_send + display_name = "NTNet Transmitter List Literal" + desc = "Creates a list literal data package and sends it through NTNet. If Encryption Key is set then transmitted data will be only picked up by receivers with the same Encryption Key." + category = "NTNet" + + /// Encryption key + var/datum/port/input/enc_key + +/obj/item/circuit_component/list_literal/ntnet_send/populate_ports() + . = ..() + enc_key = add_input_port("Encryption Key", PORT_TYPE_STRING) + +/obj/item/circuit_component/list_literal/ntnet_send/should_receive_input(datum/port/input/port) + . = ..() + if(!.) + return FALSE + /// If the server is down, don't use power or attempt to send data + return find_functional_ntnet_relay() + +/obj/item/circuit_component/list_literal/ntnet_send/input_received(datum/port/input/port) + . = ..() + send_ntnet_data(list_output, enc_key.value) diff --git a/code/modules/wiremod/core/component.dm b/code/modules/wiremod/core/component.dm index 4d8344e1e6a..cf142d1e365 100644 --- a/code/modules/wiremod/core/component.dm +++ b/code/modules/wiremod/core/component.dm @@ -405,3 +405,14 @@ */ /obj/item/circuit_component/proc/unregister_usb_parent(atom/movable/shell) return + +/** + * Called when a circuit component requests to send Ntnet data signal. + * + * Arguments: + * * port - The required list port needed by the Ntnet recieve + * * key - The encryption key + * * signal_type - The signal type used for sending this global signal (optional, default is COMSIG_GLOB_CIRCUIT_NTNET_DATA_SENT) + */ +/obj/item/circuit_component/proc/send_ntnet_data(datum/port/input/port, key, signal_type = COMSIG_GLOB_CIRCUIT_NTNET_DATA_SENT) + SEND_GLOBAL_SIGNAL(signal_type, list("data" = port.value, "enc_key" = key, "port" = WEAKREF(port))) diff --git a/code/modules/wiremod/core/integrated_circuit.dm b/code/modules/wiremod/core/integrated_circuit.dm index 7dde89e5116..5a825b81482 100644 --- a/code/modules/wiremod/core/integrated_circuit.dm +++ b/code/modules/wiremod/core/integrated_circuit.dm @@ -80,6 +80,9 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) /// The Y position of the screen. Used for adding components. var/screen_y = 0 + /// The grid mode state for the circuit. + var/grid_mode = TRUE + /// The current size of the circuit. var/current_size = 0 @@ -400,6 +403,7 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) .["examined_notices"] = examined?.get_ui_notices() .["examined_rel_x"] = examined_rel_x .["examined_rel_y"] = examined_rel_y + .["grid_mode"] = grid_mode .["is_admin"] = (admin_only || isAdminGhostAI(user)) && check_rights_for(user.client, R_VAREDIT) @@ -577,6 +581,9 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) else set_display_name("") . = TRUE + if("toggle_grid_mode") + toggle_grid_mode() + . = TRUE if("set_examined_component") var/component_id = text2num(params["component_id"]) if(!WITHIN_RANGE(component_id, attached_components)) @@ -709,6 +716,10 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) else shell.name = initial(shell.name) +/// Toggles the grid mode property for this circuit. +/obj/item/integrated_circuit/proc/toggle_grid_mode() + grid_mode = !grid_mode + /** * Returns the creator of the integrated circuit. Used in admin messages and other related things. */ diff --git a/tgstation.dme b/tgstation.dme index 3dfe9614fc2..839c7b6628a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6087,6 +6087,7 @@ #include "code\modules\wiremod\components\atom\direction.dm" #include "code\modules\wiremod\components\atom\gps.dm" #include "code\modules\wiremod\components\atom\health.dm" +#include "code\modules\wiremod\components\atom\health_state.dm" #include "code\modules\wiremod\components\atom\hear.dm" #include "code\modules\wiremod\components\atom\matscanner.dm" #include "code\modules\wiremod\components\atom\pinpointer.dm" @@ -6130,9 +6131,11 @@ #include "code\modules\wiremod\components\math\logic.dm" #include "code\modules\wiremod\components\math\not.dm" #include "code\modules\wiremod\components\math\random.dm" +#include "code\modules\wiremod\components\math\toggle.dm" #include "code\modules\wiremod\components\math\trigonometry.dm" #include "code\modules\wiremod\components\ntnet\ntnet_receive.dm" #include "code\modules\wiremod\components\ntnet\ntnet_send.dm" +#include "code\modules\wiremod\components\ntnet\ntnet_send_literal.dm" #include "code\modules\wiremod\components\sensors\pressuresensor.dm" #include "code\modules\wiremod\components\sensors\tempsensor.dm" #include "code\modules\wiremod\components\sensors\view_sensor.dm" diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx b/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx index d51c39714ef..22f7c6ce1a8 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/ObjectComponent.jsx @@ -39,8 +39,8 @@ export class ObjectComponent extends Component { if (dragPos) { act('set_component_coordinates', { component_id: index, - rel_x: dragPos.x, - rel_y: dragPos.y, + rel_x: this.roundToGrid(dragPos.x), + rel_y: this.roundToGrid(dragPos.y), }); } @@ -81,6 +81,12 @@ export class ObjectComponent extends Component { ); } + // Round the units to the grid (bypass if grid mode is off) + roundToGrid(input_value) { + if (!this.props.gridMode) return input_value; + return Math.round(input_value / 10) * 10; + } + render() { const { input_ports, @@ -99,14 +105,15 @@ export class ObjectComponent extends Component { onPortRightClick = noop, onPortMouseUp = noop, act = noop, + gridMode = true, ...rest } = this.props; const { startPos, dragPos } = this.state; let [x_pos, y_pos] = [x, y]; if (dragPos && startPos && startPos.x === x_pos && startPos.y === y_pos) { - x_pos = dragPos.x; - y_pos = dragPos.y; + x_pos = this.roundToGrid(dragPos.x); + y_pos = this.roundToGrid(dragPos.y); } // Assigned onto the ports diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx b/tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx index 79e4df4109f..316a1754b45 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/index.jsx @@ -401,6 +401,7 @@ export class IntegratedCircuit extends Component { examined_rel_y, screen_x, screen_y, + grid_mode, is_admin, variables, global_basic_types, @@ -473,6 +474,7 @@ export class IntegratedCircuit extends Component { position="absolute" top={0} color="transparent" + tooltip="Show Variables Menu" icon="cog" selected={variableMenuOpen} onClick={() => @@ -487,6 +489,7 @@ export class IntegratedCircuit extends Component { position="absolute" top={0} color="transparent" + tooltip="Show Components Menu" icon="plus" selected={componentMenuOpen} onClick={() => @@ -496,6 +499,17 @@ export class IntegratedCircuit extends Component { } /> + +