[MIRROR] Added circuit component UI details, added multiplexer and allowed inserting components directly into shells. (#6479)

* Added circuit component UI details, added multiplexer and allowed inserting components directly into shells. (#59635)

Adds the multiplexer circuit component - en.wikipedia.org/wiki/Multiplexer
Circuit components can now be directly inserted into shells rather than having to take the integrated circuit out.
Special information can be accessed from components now through the "Info" button besides the eject button on a component.

* Added circuit component UI details, added multiplexer and allowed inserting components directly into shells.

Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com>
This commit is contained in:
SkyratBot
2021-06-23 23:50:59 +02:00
committed by GitHub
parent 1a2f4f5d69
commit 5ab9aba9d4
41 changed files with 398 additions and 95 deletions
+36
View File
@@ -16,6 +16,9 @@
/// The name of the component shown on the UI
var/display_name = "Generic"
/// The description of the component shown on the UI
var/display_desc = "A generic component"
/// The integrated_circuit that this component is attached to.
var/obj/item/integrated_circuit/parent
@@ -54,11 +57,16 @@
. = ..()
if(name == COMPONENT_DEFAULT_NAME)
name = "[lowertext(display_name)] [COMPONENT_DEFAULT_NAME]"
populate_options()
if(length(options))
current_option = options[1]
return INITIALIZE_HINT_LATELOAD
/// Called when the options variable should be set.
/obj/item/circuit_component/proc/populate_options()
return
/obj/item/circuit_component/LateInitialize()
. = ..()
if(circuit_flags & CIRCUIT_FLAG_INPUT_SIGNAL)
@@ -187,3 +195,31 @@
if((circuit_flags & CIRCUIT_FLAG_INPUT_SIGNAL) && !COMPONENT_TRIGGERED_BY(trigger_input, port))
return TRUE
/**
* Gets the UI notices to be displayed on the CircuitInfo panel.
*
* Returns a list of buttons in the following format
* list(
* "icon" = ICON(string)
* "content" = CONTENT(string)
* "color" = COLOR(string, not a hex)
* )
*/
/obj/item/circuit_component/proc/get_ui_notices()
. = list()
if(!removable)
. += list(list(
"icon" = "lock",
"content" = "Unremovable",
"color" = "red"
))
if(length(input_ports))
. += list(list(
"icon" = "bolt",
"content" = "Power Usage Per Input: [power_usage_per_input]",
"color" = "orange",
))
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/light
display_name = "Light"
display_desc = "A component that emits a light of a specific brightness and colour. Requires a shell."
/// The colours of the light
var/datum/port/input/red
@@ -21,6 +22,14 @@
var/min_lightness = 0.4
var/shell_light_color
/obj/item/circuit_component/light/get_ui_notices()
. = ..()
. += list(list(
"icon" = "lightbulb",
"content" = "Maximum Brightness: [max_power]",
"color" = "orange"
))
/obj/item/circuit_component/light/Initialize()
. = ..()
red = add_input_port("Red", PORT_TYPE_NUMBER)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/pull
display_name = "Start Pulling"
display_desc = "A component that can force the shell to pull entities. Only works for drone shells."
/// Frequency input
var/datum/port/input/target
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/radio
display_name = "Radio"
display_desc = "A component that can listen and send frequencies."
/// Frequency input
var/datum/port/input/freq
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/speech
display_name = "Speech"
display_desc = "A component that sends a message. Requires a shell."
/// The message to send
var/datum/port/input/message
@@ -16,6 +17,14 @@
COOLDOWN_DECLARE(next_speech)
/obj/item/circuit_component/speech/get_ui_notices()
. = ..()
. += list(list(
"icon" = "stopwatch",
"content" = "Speech Cooldown: [DisplayTimeText(speech_cooldown)]",
"color" = "orange",
))
/obj/item/circuit_component/speech/Initialize()
. = ..()
message = add_input_port("Message", PORT_TYPE_STRING, FALSE)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/direction
display_name = "Get Direction"
display_desc = "A component that returns the direction of itself and an entity."
/// The input port
var/datum/port/input/input_port
@@ -23,6 +24,14 @@
/// Maximum range for a valid direction to be returned
var/max_range = 7
/obj/item/circuit_component/direction/get_ui_notices()
. = ..()
. += list(list(
"icon" = "info",
"content" = "Maximum Range: [max_range] tiles",
"color" = "orange",
))
/obj/item/circuit_component/direction/Initialize()
. = ..()
input_port = add_input_port("Organism", PORT_TYPE_ATOM)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/gps
display_name = "Internal GPS"
display_desc = "A component that returns the xyz co-ordinates of itself."
/// The result from the output
var/datum/port/output/x_pos
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/health
display_name = "Get Health"
display_desc = "A component that returns the health of an organism."
/// The input port
var/datum/port/input/input_port
@@ -24,6 +25,14 @@
var/max_range = 5
/obj/item/circuit_component/health/get_ui_notices()
. = ..()
. += list(list(
"icon" = "info",
"content" = "Maximum Range: [max_range] tiles",
"color" = "orange",
))
/obj/item/circuit_component/health/Initialize()
. = ..()
input_port = add_input_port("Organism", PORT_TYPE_ATOM)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/hear
display_name = "Voice Activator"
display_desc = "A component that listens for messages. Requires a shell."
flags_1 = HEAR_1
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/self
display_name = "Self"
display_desc = "A component that returns the current shell."
/// The shell this component is attached to.
var/datum/port/output/output
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/species
display_name = "Get Species"
display_desc = "A component that returns the species of its input."
/// The input port
var/datum/port/input/input_port
@@ -6,6 +6,7 @@
*/
/obj/item/circuit_component/arithmetic
display_name = "Arithmetic"
display_desc = "General arithmetic component with arithmetic capabilities."
/// The amount of input ports to have
var/input_port_amount = 4
@@ -15,17 +16,18 @@
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
GLOBAL_LIST_INIT(comp_arithmetic_options, list(
COMP_ARITHMETIC_ADD,
COMP_ARITHMETIC_SUBTRACT,
COMP_ARITHMETIC_MULTIPLY,
COMP_ARITHMETIC_DIVIDE,
COMP_ARITHMETIC_MIN,
COMP_ARITHMETIC_MAX,
))
/obj/item/circuit_component/arithmetic/populate_options()
var/static/component_options = list(
COMP_ARITHMETIC_ADD,
COMP_ARITHMETIC_SUBTRACT,
COMP_ARITHMETIC_MULTIPLY,
COMP_ARITHMETIC_DIVIDE,
COMP_ARITHMETIC_MIN,
COMP_ARITHMETIC_MAX,
)
options = component_options
/obj/item/circuit_component/arithmetic/Initialize()
options = GLOB.comp_arithmetic_options
. = ..()
for(var/port_id in 1 to input_port_amount)
var/letter = ascii2text(text2ascii("A") + (port_id-1))
@@ -5,22 +5,21 @@
*/
/obj/item/circuit_component/compare/comparison
display_name = "Comparison"
display_desc = "A component that compares two objects."
input_port_amount = 2
var/current_type = PORT_TYPE_ANY
GLOBAL_LIST_INIT(comp_comparison_options, list(
COMP_COMPARISON_EQUAL,
COMP_COMPARISON_NOT_EQUAL,
COMP_COMPARISON_GREATER_THAN,
COMP_COMPARISON_LESS_THAN,
COMP_COMPARISON_GREATER_THAN_OR_EQUAL,
COMP_COMPARISON_LESS_THAN_OR_EQUAL
))
/obj/item/circuit_component/compare/comparison/Initialize()
options = GLOB.comp_comparison_options
return ..()
/obj/item/circuit_component/compare/comparison/populate_options()
var/static/component_options = list(
COMP_COMPARISON_EQUAL,
COMP_COMPARISON_NOT_EQUAL,
COMP_COMPARISON_GREATER_THAN,
COMP_COMPARISON_LESS_THAN,
COMP_COMPARISON_GREATER_THAN_OR_EQUAL,
COMP_COMPARISON_LESS_THAN_OR_EQUAL,
)
options = component_options
/obj/item/circuit_component/compare/comparison/input_received(datum/port/input/port)
switch(current_option)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/index
display_name = "Index List"
display_desc = "A component that returns the value of a list at a given index."
/// The input port
var/datum/port/input/list_port
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/length
display_name = "Length"
display_desc = "A component that returns the length of its input."
/// The input port
var/datum/port/input/input_port
+8 -10
View File
@@ -5,17 +5,15 @@
*/
/obj/item/circuit_component/compare/logic
display_name = "Logic"
display_desc = "A component with 'and' and 'or' capabilities."
GLOBAL_LIST_INIT(comp_logic_options, list(
COMP_LOGIC_AND,
COMP_LOGIC_OR,
COMP_LOGIC_XOR
))
/obj/item/circuit_component/compare/logic/Initialize()
options = GLOB.comp_logic_options
return ..()
/obj/item/circuit_component/compare/logic/populate_options()
var/static/component_options = list(
COMP_LOGIC_AND,
COMP_LOGIC_OR,
COMP_LOGIC_XOR,
)
options = component_options
/obj/item/circuit_component/compare/logic/do_comparisons(list/ports)
. = FALSE
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/not
display_name = "Not"
display_desc = "A component that inverts its input."
/// The input port
var/datum/port/input/input_port
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/random
display_name = "Random"
display_desc = "A component that returns random values."
/// The minimum value that the random number can be
var/datum/port/input/minimum
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/concat
display_name = "Concatenate"
display_desc = "A component that combines strings."
/// The amount of input ports to have
var/input_port_amount = 4
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/compare/contains
display_name = "String Contains"
display_desc = "Checks if a string contains a word/letter"
input_port_amount = 0
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/textcase
display_name = "Text Case"
display_desc = "A component that makes its input uppercase or lowercase."
/// The input port
var/datum/port/input/input_port
@@ -14,13 +15,14 @@
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
GLOBAL_LIST_INIT(comp_text_operations, list(
COMP_TEXT_LOWER,
COMP_TEXT_UPPER
))
/obj/item/circuit_component/textcase/populate_options()
var/static/component_options = list(
COMP_TEXT_LOWER,
COMP_TEXT_UPPER,
)
options = component_options
/obj/item/circuit_component/textcase/Initialize()
options = GLOB.comp_text_operations
. = ..()
input_port = add_input_port("Input", PORT_TYPE_STRING)
output = add_output_port("Output", PORT_TYPE_STRING)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/tostring
display_name = "To String"
display_desc = "A component that converts its input to text."
/// The input port
var/datum/port/input/input_port
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/clock
display_name = "Clock"
display_desc = "A component that repeatedly fires."
/// Whether the clock is on or not
var/datum/port/input/on
@@ -12,6 +13,14 @@
/// The signal from this clock component
var/datum/port/output/signal
/obj/item/circuit_component/clock/get_ui_notices()
. = ..()
. += list(list(
"icon" = "clock",
"content" = "Clock Interval: [DisplayTimeText(COMP_CLOCK_DELAY)]",
"color" = "orange",
))
/obj/item/circuit_component/clock/Initialize()
. = ..()
on = add_input_port("On", PORT_TYPE_NUMBER)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/combiner
display_name = "Combiner"
display_desc = "A component that combines multiple inputs to provide 1 output."
/// The amount of input ports to have
var/input_port_amount = 4
@@ -13,34 +14,40 @@
var/current_type
GLOBAL_LIST_INIT(comp_combiner_options, list(
COMP_COMBINER_ANY,
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
PORT_TYPE_SIGNAL,
))
/obj/item/circuit_component/combiner/populate_options()
var/static/component_options = list(
COMP_TYPE_ANY,
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
PORT_TYPE_SIGNAL,
)
options = component_options
/obj/item/circuit_component/combiner/Initialize()
options = GLOB.comp_combiner_options
. = ..()
current_type = current_option
current_option = COMP_TYPE_ANY
current_type = COMP_TYPE_ANY
for(var/port_id in 1 to input_port_amount)
var/letter = ascii2text(text2ascii("A") + (port_id-1))
add_input_port(letter, current_type)
output_port = add_output_port("Output", current_type)
add_input_port(letter, PORT_TYPE_ANY)
output_port = add_output_port("Output", PORT_TYPE_ANY)
/obj/item/circuit_component/combiner/Destroy()
output_port = null
return ..()
/obj/item/circuit_component/combiner/input_received(datum/port/input/port)
. = ..()
if(current_type != current_option && (current_option != COMP_COMBINER_ANY || current_type != PORT_TYPE_ANY))
if(current_type != current_option && (current_option != COMP_TYPE_ANY || current_type != PORT_TYPE_ANY))
current_type = current_option
if(current_type == COMP_COMBINER_ANY)
if(current_type == COMP_TYPE_ANY)
current_type = PORT_TYPE_ANY
for(var/datum/port/input/input_port as anything in input_ports)
input_port.set_datatype(current_type)
output_port.set_datatype(current_type)
output_port.set_output(port?.input_value)
if(. || !port)
return TRUE
output_port.set_output(port.input_value)
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/delay
display_name = "Delay"
display_desc = "A component that delays a signal by a specified duration."
/// Amount to delay by
var/datum/port/input/delay_amount
@@ -0,0 +1,66 @@
/**
* # Combiner Component
*
* Combines multiple inputs into 1 output port.
*/
/obj/item/circuit_component/multiplexer
display_name = "Multiplexer"
display_desc = "A component that allows you to selectively choose which input port provides an output. The first port is the selector and takes a number between 1 and the maximum port amount."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The port to select from, goes from 1 to input_port_amount
var/datum/port/input/input_port
/// The amount of input ports to have
var/input_port_amount = 4
var/datum/port/output/output_port
/// Current type of the ports
var/current_type
/// The multiplexer inputs. These are what get selected for the output by the input_port.
var/list/datum/port/input/multiplexer_inputs
/obj/item/circuit_component/multiplexer/populate_options()
var/static/component_options = list(
COMP_TYPE_ANY,
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
)
options = component_options
/obj/item/circuit_component/multiplexer/Initialize()
. = ..()
current_option = COMP_TYPE_ANY
current_type = COMP_TYPE_ANY
input_port = add_input_port("Selector", PORT_TYPE_NUMBER, default = 1)
multiplexer_inputs = list()
for(var/port_id in 1 to input_port_amount)
multiplexer_inputs += add_input_port("Port [port_id]", PORT_TYPE_ANY)
output_port = add_output_port("Output", PORT_TYPE_ANY)
/obj/item/circuit_component/multiplexer/Destroy()
output_port = null
multiplexer_inputs.Cut()
multiplexer_inputs = null
return ..()
/obj/item/circuit_component/multiplexer/input_received(datum/port/input/port)
. = ..()
if(current_type != current_option && (current_option != COMP_TYPE_ANY || current_type != PORT_TYPE_ANY))
current_type = current_option
if(current_type == COMP_TYPE_ANY)
current_type = PORT_TYPE_ANY
for(var/datum/port/input/input_port as anything in multiplexer_inputs)
input_port.set_datatype(current_type)
output_port.set_datatype(current_type)
input_port.set_input(clamp(input_port.input_value || 1, 1, input_port_amount), FALSE)
if(.)
return TRUE
output_port.set_output(multiplexer_inputs[input_port.input_value].input_value)
@@ -7,6 +7,7 @@
*/
/obj/item/circuit_component/ram
display_name = "RAM"
display_desc = "A component that retains a variable."
/// The input to store
var/datum/port/input/input_port
@@ -5,6 +5,7 @@
*/
/obj/item/circuit_component/compare/typecheck
display_name = "Typecheck"
display_desc = "A component that checks the type of its input."
input_port_amount = 1
@@ -17,9 +18,16 @@ GLOBAL_LIST_INIT(comp_typecheck_options, list(
COMP_TYPECHECK_HUMAN,
))
/obj/item/circuit_component/compare/typecheck/Initialize()
options = GLOB.comp_typecheck_options
return ..()
/obj/item/circuit_component/compare/typecheck/populate_options()
var/static/component_options = list(
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
COMP_TYPECHECK_MOB,
COMP_TYPECHECK_HUMAN,
)
options = component_options
/obj/item/circuit_component/compare/typecheck/do_comparisons(list/ports)
if(!length(ports))
@@ -32,6 +32,15 @@
/// The ID that is authorized to unlock/lock the shell so that the circuit can/cannot be removed.
var/datum/weakref/owner_id
/// The current examined component. Used in IntegratedCircuit UI
var/datum/weakref/examined_component
/// X position of the examined_component
var/examined_rel_x = 0
/// Y position of the examined component
var/examined_rel_y = 0
/obj/item/integrated_circuit/Initialize()
. = ..()
RegisterSignal(src, COMSIG_ATOM_USB_CABLE_TRY_ATTACH, .proc/on_atom_usb_cable_try_attach)
@@ -44,7 +53,10 @@
for(var/obj/item/circuit_component/to_delete in attached_components)
remove_component(to_delete)
qdel(to_delete)
attached_components.Cut()
shell = null
examined_component = null
owner_id = null
QDEL_NULL(cell)
return ..()
@@ -228,6 +240,16 @@
.["display_name"] = display_name
var/obj/item/circuit_component/examined
if(examined_component)
examined = examined_component.resolve()
.["examined_name"] = examined?.display_name
.["examined_desc"] = examined?.display_desc
.["examined_notices"] = examined?.get_ui_notices()
.["examined_rel_x"] = examined_rel_x
.["examined_rel_y"] = examined_rel_y
/obj/item/integrated_circuit/ui_host(mob/user)
if(shell)
return shell
@@ -396,6 +418,17 @@
shell.name = initial(shell.name)
. = TRUE
if("set_examined_component")
var/component_id = text2num(params["component_id"])
if(!WITHIN_RANGE(component_id, attached_components))
return
examined_component = WEAKREF(attached_components[component_id])
examined_rel_x = text2num(params["x"])
examined_rel_y = text2num(params["y"])
. = TRUE
if("remove_examined_component")
examined_component = null
. = TRUE
/obj/item/integrated_circuit/proc/on_atom_usb_cable_try_attach(datum/source, obj/item/usb_cable/usb_cable, mob/user)
usb_cable.balloon_alert(user, "circuit needs to be in a compatible shell")
@@ -22,6 +22,7 @@
/obj/item/circuit_component/compact_remote
display_name = "Compact Remote"
display_desc = "Used to receive inputs from the compact remote shell. Use the shell in hand to trigger the output signal."
/// Called when attack_self is called on the shell.
var/datum/port/output/signal
+1
View File
@@ -23,6 +23,7 @@
/obj/item/circuit_component/controller
display_name = "Controller"
display_desc = "Used to receive inputs from the controller shell. Use the shell in hand to trigger the output signal. Alt-click for the alternate signal. Right click for the extra signal."
/// The three separate buttons that are called in attack_hand on the shell.
var/datum/port/output/signal
+2 -1
View File
@@ -27,6 +27,7 @@
/obj/item/circuit_component/bot_circuit
display_name = "Drone"
display_desc = "Used to send movement output signals to the drone shell."
/// The inputs to allow for the drone to move
var/datum/port/input/north
@@ -41,7 +42,7 @@
COOLDOWN_DECLARE(west_delay)
/// Delay between each movement
var/move_delay = COMP_CLOCK_DELAY
var/move_delay = PORT_INPUT_RECEIVE_DELAY
/obj/item/circuit_component/bot_circuit/Initialize()
. = ..()
+2
View File
@@ -38,6 +38,7 @@
/obj/item/circuit_component/money_dispenser
display_name = "Money Dispenser"
display_desc = "Used to dispense money from the money bot. Money is taken from the internal storage of money."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The amount of money to dispense
@@ -86,6 +87,7 @@
/obj/item/circuit_component/money_bot
display_name = "Money Bot"
var/obj/structure/money_bot/attached_bot
display_desc = "Used to receive input signals when money is inserted into the money bot shell and also keep track of the total money in the shell."
/// Total money in the shell
var/datum/port/output/total_money