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.
## Why It's Good For The Game
This improves the overall experience for circuits.
### UI changes
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.
All three buttons have been given tool tips.
### Tweaked Components
* 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
* 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
* 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
* 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
* 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 {
}
/>
+
+
{!!is_admin && (
),
)}