tgui backend

This commit is contained in:
LetterN
2021-10-28 12:51:31 +08:00
parent 853ff1d8ad
commit 49940c373e
110 changed files with 7353 additions and 259 deletions
@@ -0,0 +1,58 @@
/**
* # Compare Component
*
* Abstract component to build conditional components
*/
/obj/item/circuit_component/compare
display_name = "Compare"
/// The amount of input ports to have
var/input_port_amount = 4
/// The trigger for the true/false signals
var/datum/port/input/compare
/// Signals sent on compare
var/datum/port/output/true
var/datum/port/output/false
/// The result from the output
var/datum/port/output/result
var/list/datum/port/input/compare_ports = list()
/obj/item/circuit_component/compare/Initialize()
. = ..()
for(var/port_id in 1 to input_port_amount)
var/letter = ascii2text(text2ascii("A") + (port_id-1))
compare_ports += add_input_port(letter, PORT_TYPE_ANY)
load_custom_ports()
compare = add_input_port("Compare", PORT_TYPE_SIGNAL)
true = add_output_port("True", PORT_TYPE_SIGNAL)
false = add_output_port("False", PORT_TYPE_SIGNAL)
result = add_output_port("Result", PORT_TYPE_NUMBER)
/**
* Used by derivatives to load their own ports in for custom use.
*/
/obj/item/circuit_component/compare/proc/load_custom_ports()
return
/obj/item/circuit_component/compare/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/logic_result = do_comparisons(compare_ports)
if(COMPONENT_TRIGGERED_BY(compare, port))
if(logic_result)
true.set_output(COMPONENT_SIGNAL)
else
false.set_output(COMPONENT_SIGNAL)
result.set_output(logic_result)
/// Do the comparisons and return a result
/obj/item/circuit_component/compare/proc/do_comparisons(list/ports)
return FALSE
@@ -0,0 +1,306 @@
/**
* # Module Component
*
* A component that has an input, output
*/
/obj/item/circuit_component/module
display_name = "Module"
desc = "A component that has other components within it, acting like a function. Use it in your hand to control the amount of input and output ports it has, as well as being able to access the integrated circuit contained inside."
var/obj/item/integrated_circuit/module/internal_circuit
var/obj/item/circuit_component/module_input/input_component
var/obj/item/circuit_component/module_output/output_component
/// Linked ports that follow a `first_port = second_port` keyed structure.
var/list/linked_ports = list()
var/port_limit = 10
/obj/item/integrated_circuit/module
var/obj/item/circuit_component/module/attached_module
/obj/item/integrated_circuit/module/ui_host(mob/user)
. = ..()
if(. == src)
return attached_module
/obj/item/integrated_circuit/module/set_display_name(new_name)
. = ..()
attached_module.display_name = new_name
attached_module.name = "module ([new_name])"
/obj/item/integrated_circuit/module/load_component(type)
if(!attached_module)
return ..()
if(ispath(type, /obj/item/circuit_component/module_input))
return attached_module.input_component
if(ispath(type, /obj/item/circuit_component/module_output))
return attached_module.output_component
return ..()
/obj/item/integrated_circuit/module/Destroy()
attached_module = null
return ..()
/obj/item/circuit_component/module_input
display_name = "Input"
desc = "A component that receives data from the module it is attached to"
removable = FALSE
/// The currently attached module
var/obj/item/circuit_component/module/attached_module
/obj/item/circuit_component/module_input/Destroy()
attached_module = null
return ..()
/obj/item/circuit_component/module_output
display_name = "Output"
desc = "A component that outputs data to the module it is attached to."
removable = FALSE
/// The currently attached module
var/obj/item/circuit_component/module/attached_module
/obj/item/circuit_component/module_output/input_received(datum/port/input/port)
. = ..()
if(!port)
return
// We don't check the parent here because frankly, we don't care. We only sync our input with the module's output
var/datum/port/output/port_to_update = attached_module.linked_ports[port]
if(!port_to_update)
CRASH("[port.type] doesn't have a linked port in [type]!")
port_to_update.set_output(port.value)
/obj/item/circuit_component/module/input_received(datum/port/input/port)
. = ..()
if(!port)
return
var/datum/port/output/port_to_update = linked_ports[port]
if(!port_to_update)
CRASH("[port.type] doesn't have a linked port in [type]!")
port_to_update.set_output(port.value)
/obj/item/circuit_component/module_output/Destroy()
attached_module = null
return ..()
/obj/item/circuit_component/module/Initialize()
. = ..()
internal_circuit = new(src)
internal_circuit.attached_module = src
input_component = new(internal_circuit)
input_component.attached_module = src
internal_circuit.add_component(input_component)
input_component.rel_x = 0
input_component.rel_y = 200
output_component = new(internal_circuit)
output_component.attached_module = src
internal_circuit.add_component(output_component)
output_component.rel_x = 400
output_component.rel_y = 200
/obj/item/circuit_component/module/save_data_to_list(list/component_data)
. = ..()
component_data["integrated_circuit"] = internal_circuit.convert_to_json()
var/list/input_data = list()
for(var/datum/port/input/input_port as anything in input_ports)
input_data += list(list(
"name" = input_port.name,
"type" = input_port.datatype,
))
var/list/output_data = list()
for(var/datum/port/output/output_port as anything in output_ports)
output_data += list(list(
"name" = output_port.name,
"type" = output_port.datatype,
))
component_data["input_ports"] = input_data
component_data["output_ports"] = output_data
/obj/item/circuit_component/module/load_data_from_list(list/component_data)
. = ..()
var/list/input_ports = component_data["input_ports"]
for(var/list/port_data as anything in input_ports)
add_and_link_input_port(port_data["name"], port_data["type"])
var/list/output_ports = component_data["output_ports"]
for(var/list/port_data as anything in output_ports)
add_and_link_output_port(port_data["name"], port_data["type"])
if(component_data["integrated_circuit"])
internal_circuit.load_circuit_data(component_data["integrated_circuit"])
/obj/item/circuit_component/module/proc/add_and_link_input_port(name, type)
var/datum/port/new_port = add_input_port(name, type)
linked_ports[new_port] = input_component.add_output_port(name, type)
/obj/item/circuit_component/module/proc/add_and_link_output_port(name, type)
var/datum/port/new_port = output_component.add_input_port(name, type)
linked_ports[new_port] = add_output_port(name, type)
/obj/item/circuit_component/module/add_to(obj/item/integrated_circuit/added_to)
. = ..()
RegisterSignal(added_to, COMSIG_CIRCUIT_SET_CELL, .proc/handle_set_cell)
RegisterSignal(added_to, COMSIG_CIRCUIT_SET_ON, .proc/handle_set_on)
RegisterSignal(added_to, COMSIG_CIRCUIT_SET_SHELL, .proc/handle_set_shell)
internal_circuit.set_cell(added_to.cell)
internal_circuit.set_shell(added_to.shell)
internal_circuit.set_on(added_to.on)
/obj/item/circuit_component/module/removed_from(obj/item/integrated_circuit/removed_from)
internal_circuit.set_cell(null)
internal_circuit.set_on(FALSE)
internal_circuit.remove_current_shell()
UnregisterSignal(removed_from, list(
COMSIG_CIRCUIT_SET_CELL,
COMSIG_CIRCUIT_SET_ON,
COMSIG_CIRCUIT_SET_SHELL,
))
return ..()
/obj/item/circuit_component/module/proc/handle_set_cell(datum/source, obj/item/stock_parts/cell/cell)
SIGNAL_HANDLER
internal_circuit.set_cell(cell)
/obj/item/circuit_component/module/proc/handle_set_on(datum/source, new_value)
SIGNAL_HANDLER
internal_circuit.set_on(new_value)
/obj/item/circuit_component/module/proc/handle_set_shell(datum/source, atom/movable/new_shell)
SIGNAL_HANDLER
internal_circuit.set_shell(new_shell)
/obj/item/circuit_component/module/Destroy()
QDEL_NULL(input_component)
QDEL_NULL(output_component)
QDEL_NULL(internal_circuit)
linked_ports = null
return ..()
/obj/item/circuit_component/module/ui_data(mob/user)
. = list()
.["input_ports"] = list()
for(var/datum/port/input/input_port as anything in input_ports)
.["input_ports"] += list(list(
"name" = input_port.name,
"type" = input_port.datatype,
))
.["output_ports"] = list()
for(var/datum/port/output/output_port as anything in output_ports)
.["output_ports"] += list(list(
"name" = output_port.name,
"type" = output_port.datatype,
))
/obj/item/circuit_component/module/ui_static_data(mob/user)
. = list()
.["global_port_types"] = GLOB.wiremod_basic_types
/obj/item/circuit_component/module/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/circuit_component))
internal_circuit.attackby(I, user, params)
return
return ..()
#define WITHIN_RANGE(id, table) (id >= 1 && id <= length(table))
/obj/item/circuit_component/module/ui_act(action, list/params)
. = ..()
if(.)
return
switch(action)
if("open_internal_circuit")
internal_circuit.interact(usr)
. = TRUE
if("add_input_port")
if(length(input_ports) > port_limit)
return
add_and_link_input_port("Input Port", PORT_TYPE_ANY)
. = TRUE
if("remove_input_port")
var/port_id = text2num(params["port_id"])
if(!WITHIN_RANGE(port_id, input_ports))
return
var/datum/port/removed_port = input_ports[port_id]
linked_ports -= removed_port
remove_input_port(removed_port)
input_component.remove_output_port(input_component.output_ports[port_id])
. = TRUE
if("add_output_port")
if(length(output_ports) > port_limit)
return
add_and_link_output_port("Output Port", PORT_TYPE_ANY)
. = TRUE
if("remove_output_port")
var/port_id = text2num(params["port_id"])
if(!WITHIN_RANGE(port_id, output_ports))
return
var/datum/port/removed_port = output_component.input_ports[port_id]
linked_ports -= removed_port
remove_output_port(output_ports[port_id])
output_component.remove_input_port(removed_port)
. = TRUE
if("set_port_name", "set_port_type")
var/port_id = text2num(params["port_id"])
var/is_input = params["is_input"]
var/list/ports_to_use
var/list/internal_ports_to_use
if(is_input)
ports_to_use = input_ports
internal_ports_to_use = input_component.output_ports
else
ports_to_use = output_ports
internal_ports_to_use = output_component.input_ports
if(!WITHIN_RANGE(port_id, ports_to_use))
return
var/datum/port/component_port = ports_to_use[port_id]
var/datum/port/internal_component_port = internal_ports_to_use[port_id]
if(action == "set_port_type")
var/type = params["port_type"]
if(!(type in GLOB.wiremod_basic_types))
return
component_port.set_datatype(type)
internal_component_port.set_datatype(type)
else
var/port_name = params["port_name"]
if(!port_name)
return
port_name = strip_html(port_name, PORT_MAX_NAME_LENGTH)
component_port.name = port_name
internal_component_port.name = port_name
. = TRUE
if(.)
SStgui.update_uis(internal_circuit)
#undef WITHIN_RANGE
/obj/item/circuit_component/module/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "CircuitModule", name)
ui.open()
ui.set_autoupdate(FALSE)
@@ -0,0 +1,68 @@
/**
* # Light Component
*
* Emits a light of a specific brightness and colour. Requires a shell.
*/
/obj/item/circuit_component/light
display_name = "Light"
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
var/datum/port/input/green
var/datum/port/input/blue
/// The brightness
var/datum/port/input/brightness
/// Whether the light is on or not
var/datum/port/input/on
var/max_power = 5
var/min_lightness = 0.4
var/shell_light_color
/obj/item/circuit_component/light/get_ui_notices()
. = ..()
. += create_ui_notice("Maximum Brightness: [max_power]", "orange", "lightbulb")
/obj/item/circuit_component/light/Initialize()
. = ..()
red = add_input_port("Red", PORT_TYPE_NUMBER)
green = add_input_port("Green", PORT_TYPE_NUMBER)
blue = add_input_port("Blue", PORT_TYPE_NUMBER)
brightness = add_input_port("Brightness", PORT_TYPE_NUMBER)
on = add_input_port("On", PORT_TYPE_NUMBER)
/obj/item/circuit_component/light/register_shell(atom/movable/shell)
. = ..()
TRIGGER_CIRCUIT_COMPONENT(src, null)
/obj/item/circuit_component/light/unregister_shell(atom/movable/shell)
shell.set_light_on(FALSE)
return ..()
/obj/item/circuit_component/light/input_received(datum/port/input/port)
. = ..()
brightness.set_value(clamp(brightness.value || 0, 0, max_power))
red.set_value(clamp(red.value, 0, 255))
blue.set_value(clamp(blue.value, 0, 255))
green.set_value(clamp(green.value, 0, 255))
var/list/hsl = rgb2hsl(red.value || 0, green.value || 0, blue.value || 0)
var/list/light_col = hsl2rgb(hsl[1], hsl[2], max(min_lightness, hsl[3]))
shell_light_color = rgb(light_col[1], light_col[2], light_col[3])
if(.)
return
if(parent.shell)
set_atom_light(parent.shell)
/obj/item/circuit_component/light/proc/set_atom_light(atom/movable/target_atom)
// Clamp anyways just for safety
var/bright_val = min(max(brightness.value || 0, 0), max_power)
target_atom.set_light_power(bright_val)
target_atom.set_light_range(bright_val)
target_atom.set_light_color(shell_light_color)
target_atom.set_light_on(!!on.value)
@@ -0,0 +1,175 @@
/**
* # Man-Machine Interface Component
*
* Allows an MMI to be inserted into a shell, allowing it to be linked up. Requires a shell.
*/
/obj/item/circuit_component/mmi
display_name = "Man-Machine Interface"
desc = "A component that allows MMI to enter shells to send output signals."
/// The message to send to the MMI in the shell.
var/datum/port/input/message
/// Sends the current MMI a message
var/datum/port/input/send
/// Ejects the current MMI
var/datum/port/input/eject
/// Called when the MMI tries moving north
var/datum/port/output/north
/// Called when the MMI tries moving east
var/datum/port/output/east
/// Called when the MMI tries moving south
var/datum/port/output/south
/// Called when the MMI tries moving west
var/datum/port/output/west
/// Returns what the MMI last clicked on.
var/datum/port/output/clicked_atom
/// Called when the MMI clicks.
var/datum/port/output/attack
/// Called when the MMI right clicks.
var/datum/port/output/secondary_attack
/// The current MMI card
var/obj/item/mmi/brain
/// Maximum length of the message that can be sent to the MMI
var/max_length = 300
/obj/item/circuit_component/mmi/Initialize()
. = ..()
message = add_input_port("Message", PORT_TYPE_STRING)
send = add_input_port("Send Message", PORT_TYPE_SIGNAL)
eject = add_input_port("Eject", PORT_TYPE_SIGNAL)
north = add_output_port("North", PORT_TYPE_SIGNAL)
east = add_output_port("East", PORT_TYPE_SIGNAL)
south = add_output_port("South", PORT_TYPE_SIGNAL)
west = add_output_port("West", PORT_TYPE_SIGNAL)
attack = add_output_port("Attack", PORT_TYPE_SIGNAL)
secondary_attack = add_output_port("Secondary Attack", PORT_TYPE_SIGNAL)
clicked_atom = add_output_port("Target Entity", PORT_TYPE_ATOM)
/obj/item/circuit_component/mmi/Destroy()
remove_current_brain()
return ..()
/obj/item/circuit_component/mmi/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(!brain)
return
if(COMPONENT_TRIGGERED_BY(eject, port))
remove_current_brain()
if(COMPONENT_TRIGGERED_BY(send, port))
if(!message.value)
return
var/msg_str = copytext(html_encode(message.value), 1, max_length)
var/mob/living/target = brain.brainmob
if(!target)
return
to_chat(target, "[span_bold("You hear a message in your ear: ")][msg_str]")
/obj/item/circuit_component/mmi/register_shell(atom/movable/shell)
. = ..()
RegisterSignal(shell, COMSIG_PARENT_ATTACKBY, .proc/handle_attack_by)
/obj/item/circuit_component/mmi/unregister_shell(atom/movable/shell)
UnregisterSignal(shell, COMSIG_PARENT_ATTACKBY)
remove_current_brain()
return ..()
/obj/item/circuit_component/mmi/proc/handle_attack_by(atom/movable/shell, obj/item/item, mob/living/attacker)
SIGNAL_HANDLER
if(istype(item, /obj/item/mmi))
var/obj/item/mmi/target_mmi = item
if(!target_mmi.brainmob)
return
add_mmi(item)
return COMPONENT_NO_AFTERATTACK
/obj/item/circuit_component/mmi/proc/add_mmi(obj/item/mmi/to_add)
remove_current_brain()
to_add.forceMove(src)
if(to_add.brainmob)
update_mmi_mob(to_add, null, to_add.brainmob)
brain = to_add
RegisterSignal(to_add, COMSIG_PARENT_QDELETING, .proc/remove_current_brain)
RegisterSignal(to_add, COMSIG_MOVABLE_MOVED, .proc/mmi_moved)
/obj/item/circuit_component/mmi/proc/mmi_moved(atom/movable/mmi)
SIGNAL_HANDLER
if(mmi.loc != src)
remove_current_brain()
/obj/item/circuit_component/mmi/proc/remove_current_brain()
SIGNAL_HANDLER
if(!brain)
return
if(brain.brainmob)
update_mmi_mob(brain, brain.brainmob)
UnregisterSignal(brain, list(
COMSIG_PARENT_QDELETING,
COMSIG_MOVABLE_MOVED
))
if(brain.loc == src)
brain.forceMove(drop_location())
brain = null
/obj/item/circuit_component/mmi/proc/update_mmi_mob(datum/source, mob/living/old_mmi, mob/living/new_mmi)
SIGNAL_HANDLER
if(old_mmi)
old_mmi.remote_control = null
UnregisterSignal(old_mmi, COMSIG_MOB_CLICKON)
if(new_mmi)
new_mmi.remote_control = src
RegisterSignal(new_mmi, COMSIG_MOB_CLICKON, .proc/handle_mmi_attack)
/obj/item/circuit_component/mmi/relaymove(mob/living/user, direct)
if(user != brain.brainmob)
return ..()
if(direct & NORTH)
north.set_output(COMPONENT_SIGNAL)
if(direct & WEST)
west.set_output(COMPONENT_SIGNAL)
if(direct & EAST)
east.set_output(COMPONENT_SIGNAL)
if(direct & SOUTH)
south.set_output(COMPONENT_SIGNAL)
return TRUE
/obj/item/circuit_component/mmi/proc/handle_mmi_attack(mob/living/source, atom/target, list/mods)
SIGNAL_HANDLER
var/list/modifiers = params2list(mods)
if(modifiers[RIGHT_CLICK])
clicked_atom.set_output(target)
secondary_attack.set_output(COMPONENT_SIGNAL)
. = COMSIG_MOB_CANCEL_CLICKON
else if(modifiers[LEFT_CLICK] && !modifiers[SHIFT_CLICK] && !modifiers[ALT_CLICK] && !modifiers[CTRL_CLICK])
clicked_atom.set_output(target)
attack.set_output(COMPONENT_SIGNAL)
. = COMSIG_MOB_CANCEL_CLICKON
/obj/item/circuit_component/mmi/add_to(obj/item/integrated_circuit/add_to)
. = ..()
if(HAS_TRAIT(add_to, TRAIT_COMPONENT_MMI))
return FALSE
ADD_TRAIT(add_to, TRAIT_COMPONENT_MMI, src)
/obj/item/circuit_component/mmi/removed_from(obj/item/integrated_circuit/removed_from)
REMOVE_TRAIT(removed_from, TRAIT_COMPONENT_MMI, src)
remove_current_brain()
return ..()
@@ -0,0 +1,113 @@
/**
* # Pathfinding component
*
* Calcualtes a path, returns a list of entities. Each entity is the next step in the path. Can be used with the direction component to move.
*/
/obj/item/circuit_component/pathfind
display_name = "Pathfinder"
desc = "When triggered, the next step to the target's location as an entity. This can be used with the direction component and the drone shell to make it move on its own. The Id Card input port is for considering ID access when pathing, it does not give the shell actual access."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/datum/port/input/input_X
var/datum/port/input/input_Y
var/datum/port/input/id_card
var/datum/port/output/output
var/datum/port/output/finished
var/datum/port/output/failed
var/datum/port/output/reason_failed
var/list/path
var/turf/old_dest
var/turf/next_turf
// Cooldown to limit how frequently we can path to the same location.
var/same_path_cooldown = 5 SECONDS
var/different_path_cooldown = 30 SECONDS
var/max_range = 60
/obj/item/circuit_component/pathfind/get_ui_notices()
. = ..()
// Not necessary to show the same path cooldown, since it doesn't change much for the player
. += create_ui_notice("Pathfinding Cooldown: [DisplayTimeText(different_path_cooldown)]", "orange", "stopwatch")
. += create_ui_notice("Maximum Range: [max_range] tiles", "orange", "info")
/obj/item/circuit_component/pathfind/Initialize()
. = ..()
input_X = add_input_port("Target X", PORT_TYPE_NUMBER, FALSE)
input_Y = add_input_port("Target Y", PORT_TYPE_NUMBER, FALSE)
id_card = add_input_port("ID Card", PORT_TYPE_ATOM, FALSE)
output = add_output_port("Next step", PORT_TYPE_ATOM)
finished = add_output_port("Arrived to destination", PORT_TYPE_SIGNAL)
failed = add_output_port("Failed", PORT_TYPE_SIGNAL)
reason_failed = add_output_port("Fail reason", PORT_TYPE_STRING)
/obj/item/circuit_component/pathfind/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/target_X = input_X.value
if(isnull(target_X))
return
var/target_Y = input_Y.value
if(isnull(target_Y))
return
var/atom/path_id = id_card.value
if(path_id && !istype(path_id, /obj/item/card/id))
path_id = null
failed.set_output(COMPONENT_SIGNAL)
reason_failed.set_output("Object marked is not an ID! Using no ID instead.")
// Get both the current turf and the destination's turf
var/turf/current_turf = get_turf(src)
var/turf/destination = locate(target_X, target_Y, current_turf?.z)
// We're already here! No need to do anything.
if(current_turf == destination)
finished.set_output(COMPONENT_SIGNAL)
old_dest = null
TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)
next_turf = null
return
// If we're going to the same place and the cooldown hasn't subsided, we're probably on the same path as before
if (destination == old_dest && TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME))
// Check if the current turf is the same as the current turf we're supposed to be in. If so, then we set the next step as the next turf on the list
if(current_turf == next_turf)
popleft(path)
next_turf = get_turf(path[1])
output.set_output(next_turf)
// Restart the cooldown since we don't need a new path ( TIMER_COOLDOWN_START might restart the timer by itself and i dont need to call TIMER_COOLDOWN_END, but better safe than sorry )
TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)
TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME, same_path_cooldown)
else // Either we're not going to the same place or the cooldown is over. Either way, we need a new path
if(destination != old_dest && TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_PATHFIND_DIF))
failed.set_output(COMPONENT_SIGNAL)
reason_failed.set_output("Cooldown still active!")
return
TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)
old_dest = destination
path = get_path_to(src, destination, max_range, id=path_id)
if(length(path) == 0 || !path)// Check if we can even path there
next_turf = null
failed.set_output(COMPONENT_SIGNAL)
reason_failed.set_output("Can't go there!")
return
else
TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_PATHFIND_DIF, different_path_cooldown)
next_turf = get_turf(path[1])
output.set_output(next_turf)
TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME, same_path_cooldown)
@@ -0,0 +1,31 @@
/**
* # Pull Component
*
* Tells the shell to start pulling on a designated atom. Only works on movable shells.
*/
/obj/item/circuit_component/pull
display_name = "Start Pulling"
desc = "A component that can force the shell to pull entities. Only works for drone shells."
/// Frequency input
var/datum/port/input/target
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/pull/Initialize()
. = ..()
target = add_input_port("Target", PORT_TYPE_ATOM)
/obj/item/circuit_component/pull/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/atom/target_atom = target.value
if(!target_atom)
return
var/mob/shell = parent.shell
if(!istype(shell) || get_dist(shell, target_atom) > 1 || shell.z != target_atom.z)
return
shell.start_pulling(target_atom)
@@ -0,0 +1,74 @@
#define COMP_RADIO_PUBLIC "public"
#define COMP_RADIO_PRIVATE "private"
/**
* # Radio Component
*
* Listens out for signals on the designated frequencies and sends signals on designated frequencies
*/
/obj/item/circuit_component/radio
display_name = "Radio"
desc = "A component that can listen and send frequencies. If set to private, the component will only receive signals from other components attached to circuitboards with the same owner id."
/// The publicity options. Controls whether it's public or private.
var/datum/port/input/option/public_options
/// Frequency input
var/datum/port/input/freq
/// Signal input
var/datum/port/input/code
/// Current frequency value
var/current_freq = DEFAULT_SIGNALER_CODE
var/datum/radio_frequency/radio_connection
/obj/item/circuit_component/radio/populate_options()
var/static/component_options = list(
COMP_RADIO_PUBLIC,
COMP_RADIO_PRIVATE,
)
public_options = add_option_port("Encryption Options", component_options)
/obj/item/circuit_component/radio/Initialize()
. = ..()
freq = add_input_port("Frequency", PORT_TYPE_NUMBER, default = FREQ_SIGNALER)
code = add_input_port("Code", PORT_TYPE_NUMBER, default = DEFAULT_SIGNALER_CODE)
TRIGGER_CIRCUIT_COMPONENT(src, null)
// These are cleaned up on the parent
trigger_input = add_input_port("Send", PORT_TYPE_SIGNAL)
trigger_output = add_output_port("Received", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/radio/Destroy()
SSradio.remove_object(src, current_freq)
return ..()
/obj/item/circuit_component/radio/input_received(datum/port/input/port)
. = ..()
freq.set_value(sanitize_frequency(freq.value, TRUE))
if(.)
return
var/frequency = freq.value
SSradio.remove_object(src, current_freq)
radio_connection = SSradio.add_object(src, frequency, RADIO_SIGNALER)
current_freq = frequency
if(COMPONENT_TRIGGERED_BY(trigger_input, port))
var/datum/signal/signal = new(list("code" = round(code.value) || 0, "key" = parent?.owner_id))
radio_connection.post_signal(src, signal)
/obj/item/circuit_component/radio/receive_signal(datum/signal/signal)
. = FALSE
if(!signal)
return
if(signal.data["code"] != round(code.value || 0))
return
if(public_options.value == COMP_RADIO_PRIVATE && parent?.owner_id != signal.data["key"])
return
trigger_output.set_output(COMPONENT_SIGNAL)
#undef COMP_RADIO_PUBLIC
#undef COMP_RADIO_PRIVATE
@@ -0,0 +1,66 @@
/**
* # Sound Emitter Component
*
* A component that emits a sound when it receives an input.
*/
/obj/item/circuit_component/soundemitter
display_name = "Sound Emitter"
desc = "A component that emits a sound when it receives an input. The frequency is a multiplier which determines the speed at which the sound is played"
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// Sound to play
var/datum/port/input/option/sound_file
/// Volume of the sound when played
var/datum/port/input/volume
/// Frequency of the sound when played
var/datum/port/input/frequency
/// The cooldown for this component of how often it can play sounds.
var/sound_cooldown = 2 SECONDS
var/list/options_map
/obj/item/circuit_component/soundemitter/get_ui_notices()
. = ..()
. += create_ui_notice("Sound Cooldown: [DisplayTimeText(sound_cooldown)]", "orange", "stopwatch")
/obj/item/circuit_component/soundemitter/Initialize()
. = ..()
volume = add_input_port("Volume", PORT_TYPE_NUMBER, default = 35)
frequency = add_input_port("Frequency", PORT_TYPE_NUMBER, default = 0)
/obj/item/circuit_component/soundemitter/populate_options()
var/static/component_options = list(
"Buzz" = 'sound/machines/buzz-sigh.ogg',
"Buzz Twice" = 'sound/machines/buzz-two.ogg',
"Chime" = 'sound/machines/chime.ogg',
"Honk" = 'sound/items/bikehorn.ogg',
"Ping" = 'sound/machines/ping.ogg',
"Sad Trombone" = 'sound/misc/sadtrombone.ogg',
"Warn" = 'sound/machines/warning-buzzer.ogg',
"Slow Clap" = 'sound/machines/slowclap.ogg',
)
sound_file = add_option_port("Sound Option", component_options)
options_map = component_options
/obj/item/circuit_component/soundemitter/input_received(datum/port/input/port)
. = ..()
volume.set_value(clamp(volume.value, 0, 100))
frequency.set_value(clamp(frequency.value, -100, 100))
if(.)
return
if(TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_SOUNDEMITTER))
return
var/sound_to_play = options_map[sound_file.value]
if(!sound_to_play)
return
playsound(src, sound_to_play, volume.value, frequency != 0, frequency = frequency.value)
TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_SOUNDEMITTER, sound_cooldown)
@@ -0,0 +1,40 @@
/**
* # Speech Component
*
* Sends a message. Requires a shell.
*/
/obj/item/circuit_component/speech
display_name = "Speech"
desc = "A component that sends a message. Requires a shell."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The message to send
var/datum/port/input/message
/// The cooldown for this component of how often it can send speech messages.
var/speech_cooldown = 1 SECONDS
/obj/item/circuit_component/speech/get_ui_notices()
. = ..()
. += create_ui_notice("Speech Cooldown: [DisplayTimeText(speech_cooldown)]", "orange", "stopwatch")
/obj/item/circuit_component/speech/Initialize()
. = ..()
message = add_input_port("Message", PORT_TYPE_STRING, FALSE)
/obj/item/circuit_component/speech/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_SPEECH))
return
if(message.value)
var/atom/movable/shell = parent.shell
// Prevents appear as the individual component if there is a shell.
if(shell)
shell.say(message.value)
else
say(message.value)
TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_SPEECH, speech_cooldown)
@@ -0,0 +1,42 @@
/**
* # Get Variable Component
*
* A component that gets a variable on an object
*/
/obj/item/circuit_component/get_variable
display_name = "Get Variable"
desc = "A component that gets a variable on an object."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN
/// Entity to get variable of
var/datum/port/input/entity
/// Variable name
var/datum/port/input/variable_name
/// Variable value
var/datum/port/output/output_value
/obj/item/circuit_component/get_variable/Initialize()
. = ..()
entity = add_input_port("Target", PORT_TYPE_ATOM)
variable_name = add_input_port("Variable Name", PORT_TYPE_STRING)
output_value = add_output_port("Output Value", PORT_TYPE_ANY)
/obj/item/circuit_component/get_variable/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/atom/object = entity.value
var/var_name = variable_name.value
if(!var_name || !object)
output_value.set_output(null)
return
if(!object.can_vv_get(var_name) || !(var_name in object.vars))
output_value.set_output(null)
return
output_value.set_output(object.vars[var_name])
@@ -0,0 +1,72 @@
#define COMP_PROC_GLOBAL "Global"
#define COMP_PROC_OBJECT "Object"
/**
* # Proc Call Component
*
* A component that calls a proc on an object and outputs the return value
*/
/obj/item/circuit_component/proccall
display_name = "Proc Call"
desc = "A component that calls a proc on an object."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN
var/datum/port/input/option/proccall_options
/// Entity to proccall on
var/datum/port/input/entity
/// Proc to call
var/datum/port/input/proc_name
/// Arguments
var/datum/port/input/arguments
/// Returns the output from the proccall
var/datum/port/output/output_value
/obj/item/circuit_component/proccall/populate_options()
var/static/list/component_options = list(
COMP_PROC_OBJECT,
COMP_PROC_GLOBAL,
)
proccall_options = add_option_port("Proccall Options", component_options)
/obj/item/circuit_component/proccall/Initialize()
. = ..()
entity = add_input_port("Target", PORT_TYPE_ATOM)
proc_name = add_input_port("Proc Name", PORT_TYPE_STRING)
arguments = add_input_port("Arguments", PORT_TYPE_LIST)
output_value = add_output_port("Output Value", PORT_TYPE_ANY)
/obj/item/circuit_component/proccall/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/called_on
if(proccall_options.value == COMP_PROC_OBJECT)
called_on = entity.value
else
called_on = GLOBAL_PROC
if(!called_on)
return
var/to_invoke = proc_name.value
var/params = arguments.value || list()
if(!to_invoke)
return
GLOB.AdminProcCaller = "CHAT_[parent.display_name]" //_ won't show up in ckeys so it'll never match with a real admin
var/result = WrapAdminProcCall(called_on, to_invoke, params)
GLOB.AdminProcCaller = null
output_value.set_output(result)
#undef COMP_PROC_GLOBAL
#undef COMP_PROC_OBJECT
@@ -0,0 +1,36 @@
/**
* # SDQL Component
*
* A component that performs an sdql operation
*/
/obj/item/circuit_component/sdql_operation
display_name = "SDQL Operation"
desc = "A component that performs an SDQL operation when invoked."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN
/// SDQL Operation to invoke
var/datum/port/input/sdql_operation
var/datum/port/output/results
/obj/item/circuit_component/sdql_operation/Initialize()
. = ..()
sdql_operation = add_input_port("SDQL String", PORT_TYPE_STRING)
results = add_output_port("Result", PORT_TYPE_LIST)
/obj/item/circuit_component/sdql_operation/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/operation = sdql_operation.value
if(GLOB.AdminProcCaller || !operation)
return TRUE
GLOB.AdminProcCaller = "CHAT_[parent.display_name]" //_ won't show up in ckeys so it'll never match with a real admin
var/list/result = world.SDQL2_query(operation, parent.get_creator_admin(), parent.get_creator())
GLOB.AdminProcCaller = null
results.set_output(result)
@@ -0,0 +1,36 @@
/**
* # Set Variable Component
*
* A component that sets a variable on an object
*/
/obj/item/circuit_component/set_variable
display_name = "Set Variable"
desc = "A component that sets a variable on an object."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN
/// Entity to set variable of
var/datum/port/input/entity
/// Variable name
var/datum/port/input/variable_name
/// New value to set the variable name to.
var/datum/port/input/new_value
/obj/item/circuit_component/set_variable/Initialize()
. = ..()
entity = add_input_port("Target", PORT_TYPE_ATOM)
variable_name = add_input_port("Variable Name", PORT_TYPE_STRING)
new_value = add_input_port("New Value", PORT_TYPE_ANY)
/obj/item/circuit_component/set_variable/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/atom/object = entity.value
var/var_name = variable_name.value
if(!var_name || !object)
return
object.vv_edit_var(var_name, new_value.value)
@@ -0,0 +1,47 @@
/**
* # Spawn Atom Component
*
* Spawns an atom.
*/
/obj/item/circuit_component/spawn_atom
display_name = "Spawn Atom"
desc = "Spawns an atom at a desired location"
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN
/// The input path to convert into a typepath
var/datum/port/input/input_path
/// The turf to spawn them at
var/datum/port/input/spawn_at
/// Parameters to pass to the atom being spawned
var/datum/port/input/parameters
/// The result from the output
var/datum/port/output/spawned_atom
/obj/item/circuit_component/spawn_atom/Initialize()
. = ..()
input_path = add_input_port("Type", PORT_TYPE_ANY)
spawn_at = add_input_port("Spawn At", PORT_TYPE_ATOM)
parameters = add_input_port("Parameters", PORT_TYPE_LIST)
spawned_atom = add_output_port("Spawned Atom", PORT_TYPE_ATOM)
/obj/item/circuit_component/spawn_atom/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/typepath = input_path.value
if(!ispath(typepath, /atom))
return
var/list/params = parameters.value
if(!params)
params = list()
params.Insert(1, spawn_at.value)
spawned_atom.set_output(new typepath(arglist(params)))
@@ -0,0 +1,28 @@
/**
* # To Type Component
*
* Converts a string into a typepath. Useful for adding components.
*/
/obj/item/circuit_component/to_type
display_name = "String To Type"
desc = "Converts a string into a typepath. Useful for adding components."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN
/// The input path to convert into a typepath
var/datum/port/input/input_path
/// The type output
var/datum/port/output/type_output
/obj/item/circuit_component/to_type/Initialize()
. = ..()
input_path = add_input_port("Type", PORT_TYPE_STRING)
type_output = add_output_port("Typepath", PORT_TYPE_ANY)
/obj/item/circuit_component/to_type/input_received(datum/port/input/port)
. = ..()
if(.)
return
type_output.set_output(text2path(input_path.value))
@@ -0,0 +1,66 @@
/**
* # Direction Component
*
* Return the direction of a mob relative to the component
*/
/obj/item/circuit_component/direction
display_name = "Get Direction"
desc = "A component that returns the direction of itself and an entity."
/// The input port
var/datum/port/input/input_port
/// The result from the output
var/datum/port/output/output
// Directions outputs
var/datum/port/output/north
var/datum/port/output/south
var/datum/port/output/east
var/datum/port/output/west
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// Maximum range for a valid direction to be returned
var/max_range = 7
/obj/item/circuit_component/direction/get_ui_notices()
. = ..()
. += create_ui_notice("Maximum Range: [max_range] tiles", "orange", "info")
/obj/item/circuit_component/direction/Initialize()
. = ..()
input_port = add_input_port("Organism", PORT_TYPE_ATOM)
output = add_output_port("Direction", PORT_TYPE_STRING)
north = add_output_port("North", PORT_TYPE_SIGNAL)
east = add_output_port("East", PORT_TYPE_SIGNAL)
south = add_output_port("South", PORT_TYPE_SIGNAL)
west = add_output_port("West", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/direction/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/atom/object = input_port.value
if(!object)
return
var/turf/location = get_turf(src)
if(object.z != location.z || get_dist(location, object) > max_range)
output.set_output(null)
return
var/direction = get_dir(location, get_turf(object))
output.set_output(dir2text(direction))
if(direction & NORTH)
north.set_output(COMPONENT_SIGNAL)
if(direction & SOUTH)
south.set_output(COMPONENT_SIGNAL)
if(direction & EAST)
east.set_output(COMPONENT_SIGNAL)
if(direction & WEST)
west.set_output(COMPONENT_SIGNAL)
@@ -0,0 +1,34 @@
/**
* # GPS Component
*
* Return the location of this
*/
/obj/item/circuit_component/gps
display_name = "Internal GPS"
desc = "A component that returns the xyz co-ordinates of itself."
/// The result from the output
var/datum/port/output/x_pos
var/datum/port/output/y_pos
var/datum/port/output/z_pos
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/gps/Initialize()
. = ..()
x_pos = add_output_port("X", PORT_TYPE_NUMBER)
y_pos = add_output_port("Y", PORT_TYPE_NUMBER)
z_pos = add_output_port("Z", PORT_TYPE_NUMBER)
/obj/item/circuit_component/gps/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/turf/location = get_turf(src)
x_pos.set_output(location?.x)
y_pos.set_output(location?.y)
z_pos.set_output(location?.z)
@@ -0,0 +1,62 @@
/**
* # Get Health Component
*
* Return the health of a mob
*/
/obj/item/circuit_component/health
display_name = "Get Health"
desc = "A component that returns the health of an organism."
/// The input port
var/datum/port/input/input_port
/// Brute damage
var/datum/port/output/brute
/// Burn damage
var/datum/port/output/burn
/// Toxin damage
var/datum/port/output/toxin
/// Oxyloss damage
var/datum/port/output/oxy
/// Health
var/datum/port/output/health
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/max_range = 5
/obj/item/circuit_component/health/get_ui_notices()
. = ..()
. += create_ui_notice("Maximum Range: [max_range] tiles", "orange", "info")
/obj/item/circuit_component/health/Initialize()
. = ..()
input_port = add_input_port("Organism", PORT_TYPE_ATOM)
brute = add_output_port("Brute Damage", PORT_TYPE_NUMBER)
burn = add_output_port("Burn Damage", PORT_TYPE_NUMBER)
toxin = add_output_port("Toxin Damage", PORT_TYPE_NUMBER)
oxy = add_output_port("Suffocation Damage", PORT_TYPE_NUMBER)
health = add_output_port("Health", PORT_TYPE_NUMBER)
/obj/item/circuit_component/health/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/mob/living/organism = input_port.value
var/turf/current_turf = get_turf(src)
if(!istype(organism) || get_dist(current_turf, organism) > max_range || current_turf.z != organism.z)
brute.set_output(null)
burn.set_output(null)
toxin.set_output(null)
oxy.set_output(null)
health.set_output(null)
return
brute.set_output(organism.getBruteLoss())
burn.set_output(organism.getFireLoss())
toxin.set_output(organism.getToxLoss())
oxy.set_output(organism.getOxyLoss())
health.set_output(organism.health)
@@ -0,0 +1,35 @@
/**
* # Hear Component
*
* Listens for messages. Requires a shell.
*/
/obj/item/circuit_component/hear
display_name = "Voice Activator"
desc = "A component that listens for messages. Requires a shell."
/// The message heard
var/datum/port/output/message_port
/// The language heard
var/datum/port/output/language_port
/// The speaker
var/datum/port/output/speaker_port
/// The trigger sent when this event occurs
var/datum/port/output/trigger_port
/obj/item/circuit_component/hear/Initialize()
. = ..()
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)
trigger_port = add_output_port("Triggered", PORT_TYPE_SIGNAL)
become_hearing_sensitive(ROUNDSTART_TRAIT)
/obj/item/circuit_component/hear/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods)
if(speaker == parent?.shell)
return
message_port.set_output(raw_message)
if(message_language)
language_port.set_output(initial(message_language.name))
speaker_port.set_output(speaker)
trigger_port.set_output(COMPONENT_SIGNAL)
@@ -0,0 +1,21 @@
/**
* # Self Component
*
* Return the current shell.
*/
/obj/item/circuit_component/self
display_name = "Self"
desc = "A component that returns the current shell."
/// The shell this component is attached to.
var/datum/port/output/output
/obj/item/circuit_component/self/Initialize()
. = ..()
output = add_output_port("Self", PORT_TYPE_ATOM)
/obj/item/circuit_component/self/register_shell(atom/movable/shell)
output.set_output(shell)
/obj/item/circuit_component/self/unregister_shell(atom/movable/shell)
output.set_output(null)
@@ -0,0 +1,35 @@
/**
* # Get Species Component
*
* Return the species of a mob
*/
/obj/item/circuit_component/species
display_name = "Get Species"
desc = "A component that returns the species of its input."
/// The input port
var/datum/port/input/input_port
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/species/Initialize()
. = ..()
input_port = add_input_port("Organism", PORT_TYPE_ATOM)
output = add_output_port("Species", PORT_TYPE_STRING)
/obj/item/circuit_component/species/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/mob/living/carbon/human/human = input_port.value
if(!istype(human) || !human.has_dna())
output.set_output(null)
return
output.set_output(human.dna.species.name)
@@ -0,0 +1,62 @@
#define COMP_BAR_OVERLAY_VERTICAL "Vertical"
#define COMP_BAR_OVERLAY_HORIZONTAL "Horizontal"
/**
* # Bar Overlay Component
*
* Basically an advanced verion of object overlay component that shows a horizontal/vertical bar.
* Requires a BCI shell.
*/
/obj/item/circuit_component/object_overlay/bar
display_name = "Bar Overlay"
desc = "Requires a BCI shell. A component that shows a bar overlay ontop of an object from a range of 0 to 100."
var/datum/port/input/option/bar_overlay_options
var/datum/port/input/bar_number
var/overlay_limit = 10
/obj/item/circuit_component/object_overlay/bar/Initialize()
. = ..()
bar_number = add_input_port("Number", PORT_TYPE_NUMBER)
/obj/item/circuit_component/object_overlay/bar/populate_options()
var/static/component_options_bar = list(
COMP_BAR_OVERLAY_VERTICAL = "barvert",
COMP_BAR_OVERLAY_HORIZONTAL = "barhoriz"
)
bar_overlay_options = add_option_port("Bar Overlay Options", component_options_bar)
options_map = component_options_bar
/obj/item/circuit_component/object_overlay/bar/show_to_owner(atom/target_atom, mob/living/owner)
if(LAZYLEN(active_overlays) >= overlay_limit)
return
var/current_option = bar_overlay_options.value
if(active_overlays[target_atom])
QDEL_NULL(active_overlays[target_atom])
var/number_clear = clamp(bar_number.value, 0, 100)
if(current_option == COMP_BAR_OVERLAY_HORIZONTAL)
number_clear = round(number_clear / 6.25) * 6.25
else if(current_option == COMP_BAR_OVERLAY_VERTICAL)
number_clear = round(number_clear / 10) * 10
var/image/cool_overlay = image(icon = 'icons/hud/screen_bci.dmi', loc = target_atom, icon_state = "[options_map[current_option]][number_clear]", layer = RIPPLE_LAYER)
if(image_pixel_x.value)
cool_overlay.pixel_x = image_pixel_x.value
if(image_pixel_y.value)
cool_overlay.pixel_y = image_pixel_y.value
active_overlays[target_atom] = WEAKREF(target_atom.add_alt_appearance(
/datum/atom_hud/alternate_appearance/basic/one_person,
"bar_overlay_[REF(src)]",
cool_overlay,
owner,
))
#undef COMP_BAR_OVERLAY_VERTICAL
#undef COMP_BAR_OVERLAY_HORIZONTAL
@@ -0,0 +1,103 @@
/**
* # Counter Overlay Component
*
* Shows an counter overlay.
* Requires a BCI shell.
*/
/obj/item/circuit_component/counter_overlay
display_name = "Counter Overlay"
desc = "A component that shows an three digit counter. Requires a BCI shell."
required_shells = list(/obj/item/organ/cyberimp/bci)
var/datum/port/input/counter_number
var/datum/port/input/image_pixel_x
var/datum/port/input/image_pixel_y
var/datum/port/input/signal_update
var/obj/item/organ/cyberimp/bci/bci
var/list/numbers = list()
var/counter_appearance
/obj/item/circuit_component/counter_overlay/Initialize()
. = ..()
counter_number = add_input_port("Displayed Number", PORT_TYPE_NUMBER)
signal_update = add_input_port("Update Overlay", PORT_TYPE_SIGNAL)
image_pixel_x = add_input_port("X-Axis Shift", PORT_TYPE_NUMBER)
image_pixel_y = add_input_port("Y-Axis Shift", PORT_TYPE_NUMBER)
/obj/item/circuit_component/counter_overlay/register_shell(atom/movable/shell)
if(istype(shell, /obj/item/organ/cyberimp/bci))
bci = shell
RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
/obj/item/circuit_component/counter_overlay/unregister_shell(atom/movable/shell)
bci = null
QDEL_NULL(counter_appearance)
for(var/number in numbers)
QDEL_NULL(number)
UnregisterSignal(shell, COMSIG_ORGAN_REMOVED)
/obj/item/circuit_component/counter_overlay/input_received(datum/port/input/port)
. = ..()
if(. || !bci)
return
var/mob/living/owner = bci.owner
if(!owner || !istype(owner) || !owner.client)
return
for(var/number in numbers)
QDEL_NULL(number)
numbers = list()
QDEL_NULL(counter_appearance)
var/image/counter = image(icon = 'icons/hud/screen_bci.dmi', icon_state = "hud_numbers", loc = owner)
if(image_pixel_x.value)
counter.pixel_x = image_pixel_x.value
if(image_pixel_y.value)
counter.pixel_y = image_pixel_y.value
counter_appearance = WEAKREF(owner.add_alt_appearance(
/datum/atom_hud/alternate_appearance/basic/one_person,
"counter_overlay_[REF(src)]",
counter,
owner,
))
var/cleared_number = clamp(round(counter_number.value), 0, 999)
for(var/i = 1 to 3)
var/cur_num = round(cleared_number / (10 ** (3 - i))) % 10
var/image/number = image(icon = 'icons/hud/screen_bci.dmi', icon_state = "hud_number_[cur_num]", loc = owner)
if(image_pixel_x.value)
number.pixel_x = image_pixel_x.value + (i - 1) * 9
if(image_pixel_y.value)
number.pixel_y = image_pixel_y.value
numbers.Add(WEAKREF(owner.add_alt_appearance(
/datum/atom_hud/alternate_appearance/basic/one_person,
"counter_overlay_[REF(src)]_[i]",
number,
owner,
)))
/obj/item/circuit_component/counter_overlay/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
SIGNAL_HANDLER
QDEL_NULL(counter_appearance)
for(var/number in numbers)
QDEL_NULL(number)
/obj/item/circuit_component/counter_overlay/Destroy()
QDEL_NULL(counter_appearance)
for(var/number in numbers)
QDEL_NULL(number)
return ..()
@@ -0,0 +1,122 @@
/**
* # Object Overlay Component
*
* Shows an overlay ontop of an object. Toggleable.
* Requires a BCI shell.
*/
#define OBJECT_OVERLAY_LIMIT 10
/obj/item/circuit_component/object_overlay
display_name = "Object Overlay"
desc = "Requires a BCI shell. A component that shows an overlay on top of an object."
required_shells = list(/obj/item/organ/cyberimp/bci)
var/datum/port/input/option/object_overlay_options
/// Target atom
var/datum/port/input/target
var/datum/port/input/image_pixel_x
var/datum/port/input/image_pixel_y
/// On/Off signals
var/datum/port/input/signal_on
var/datum/port/input/signal_off
var/obj/item/organ/cyberimp/bci/bci
var/list/active_overlays = list()
var/list/options_map
/obj/item/circuit_component/object_overlay/Initialize()
. = ..()
target = add_input_port("Target", PORT_TYPE_ATOM)
signal_on = add_input_port("Create Overlay", PORT_TYPE_SIGNAL)
signal_off = add_input_port("Remove Overlay", PORT_TYPE_SIGNAL)
image_pixel_x = add_input_port("X-Axis Shift", PORT_TYPE_NUMBER)
image_pixel_y = add_input_port("Y-Axis Shift", PORT_TYPE_NUMBER)
/obj/item/circuit_component/object_overlay/Destroy()
for(var/active_overlay in active_overlays)
QDEL_NULL(active_overlay)
return ..()
/obj/item/circuit_component/object_overlay/populate_options()
var/static/component_options = list(
"Corners (Blue)" = "hud_corners",
"Corners (Red)" = "hud_corners_red",
"Circle (Blue)" = "hud_circle",
"Circle (Red)" = "hud_circle_red",
"Small Corners (Blue)" = "hud_corners_small",
"Small Corners (Red)" = "hud_corners_small_red",
"Triangle (Blue)" = "hud_triangle",
"Triangle (Red)" = "hud_triangle_red",
"HUD mark (Blue)" = "hud_mark",
"HUD mark (Red)" = "hud_mark_red"
)
object_overlay_options = add_option_port("Object", component_options)
options_map = component_options
/obj/item/circuit_component/object_overlay/register_shell(atom/movable/shell)
if(istype(shell, /obj/item/organ/cyberimp/bci))
bci = shell
RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
/obj/item/circuit_component/object_overlay/unregister_shell(atom/movable/shell)
bci = null
UnregisterSignal(shell, COMSIG_ORGAN_REMOVED)
/obj/item/circuit_component/object_overlay/input_received(datum/port/input/port)
. = ..()
if(. || !bci)
return
var/mob/living/owner = bci.owner
var/atom/target_atom = target.value
if(!owner || !istype(owner) || !owner.client || !target_atom)
return
if(COMPONENT_TRIGGERED_BY(signal_on, port))
show_to_owner(target_atom, owner)
if(COMPONENT_TRIGGERED_BY(signal_off, port) && (target_atom in active_overlays))
QDEL_NULL(active_overlays[target_atom])
active_overlays.Remove(target_atom)
/obj/item/circuit_component/object_overlay/proc/show_to_owner(atom/target_atom, mob/living/owner)
if(LAZYLEN(active_overlays) >= OBJECT_OVERLAY_LIMIT)
return
if(active_overlays[target_atom])
QDEL_NULL(active_overlays[target_atom])
var/image/cool_overlay = image(icon = 'icons/hud/screen_bci.dmi', loc = target_atom, icon_state = options_map[object_overlay_options.value], layer = RIPPLE_LAYER)
if(image_pixel_x.value)
cool_overlay.pixel_x = image_pixel_x.value
if(image_pixel_y.value)
cool_overlay.pixel_y = image_pixel_y.value
var/alt_appearance = WEAKREF(target_atom.add_alt_appearance(
/datum/atom_hud/alternate_appearance/basic/one_person,
"object_overlay_[REF(src)]",
cool_overlay,
owner,
))
active_overlays[target_atom] = alt_appearance
/obj/item/circuit_component/object_overlay/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
SIGNAL_HANDLER
for(var/atom/target_atom in active_overlays)
QDEL_NULL(active_overlays[target_atom])
active_overlays.Remove(target_atom)
#undef OBJECT_OVERLAY_LIMIT
@@ -0,0 +1,64 @@
/**
* # Target Intercept Component
*
* When activated intercepts next click and outputs clicked atom.
* Requires a BCI shell.
*/
/obj/item/circuit_component/target_intercept
display_name = "Target Intercept"
desc = "Requires a BCI shell. When activated, this component will allow user to target an object using their brain and will output the reference to said object."
required_shells = list(/obj/item/organ/cyberimp/bci)
var/datum/port/output/clicked_atom
var/obj/item/organ/cyberimp/bci/bci
var/intercept_cooldown = 1 SECONDS
/obj/item/circuit_component/target_intercept/Initialize()
. = ..()
trigger_input = add_input_port("Activate", PORT_TYPE_SIGNAL)
trigger_output = add_output_port("Triggered", PORT_TYPE_SIGNAL)
clicked_atom = add_output_port("Targeted Object", PORT_TYPE_ATOM)
/obj/item/circuit_component/target_intercept/register_shell(atom/movable/shell)
if(istype(shell, /obj/item/organ/cyberimp/bci))
bci = shell
RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
/obj/item/circuit_component/target_intercept/unregister_shell(atom/movable/shell)
bci = null
UnregisterSignal(shell, COMSIG_ORGAN_REMOVED)
/obj/item/circuit_component/target_intercept/input_received(datum/port/input/port)
. = ..()
if(. || !bci)
return
var/mob/living/owner = bci.owner
if(!owner || !istype(owner) || !owner.client)
return
if(TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_TARGET_INTERCEPT))
return
to_chat(owner, "<B>Left-click to trigger target interceptor!</B>")
owner.client.click_intercept = src
/obj/item/circuit_component/target_intercept/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
SIGNAL_HANDLER
if(owner.client && owner.client.click_intercept == src)
owner.client.click_intercept = null
/obj/item/circuit_component/target_intercept/proc/InterceptClickOn(mob/user, params, atom/object)
user.client.click_intercept = null
clicked_atom.set_output(object)
trigger_output.set_output(COMPONENT_SIGNAL)
TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_TARGET_INTERCEPT, intercept_cooldown)
/obj/item/circuit_component/target_intercept/get_ui_notices()
. = ..()
. += create_ui_notice("Target Interception Cooldown: [DisplayTimeText(intercept_cooldown)]", "orange", "stopwatch")
@@ -0,0 +1,48 @@
/**
* # Concat List Component
*
* Concatenates a list with a separator
*/
/obj/item/circuit_component/concat_list
display_name = "Concatenate List"
desc = "A component that joins up a list with a separator into a single string."
/// The input port
var/datum/port/input/list_port
/// The seperator
var/datum/port/input/separator
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/concat_list/Initialize()
. = ..()
list_port = add_input_port("List", PORT_TYPE_LIST)
separator = add_input_port("Seperator", PORT_TYPE_STRING)
output = add_output_port("Output", PORT_TYPE_STRING)
/obj/item/circuit_component/concat_list/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/seperator = separator.value
if(!seperator)
return
var/list/list_input = list_port.value
if(!list_input)
return
var/list/text_list = list()
for(var/entry in list_input)
if(isatom(entry))
text_list += PORT_TYPE_ATOM
else
text_list += "[entry]"
output.set_output(text_list.Join(seperator))
@@ -0,0 +1,42 @@
/**
* # Get Column Component
*
* Gets the column of a table and returns it as a regular list.
*/
/obj/item/circuit_component/get_column
display_name = "Get Column"
desc = "Gets the column of a table and returns it as a regular list."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The list to perform the filter on
var/datum/port/input/received_table
/// The name of the column to check
var/datum/port/input/column_name
/// The filtered list
var/datum/port/output/output_list
/obj/item/circuit_component/get_column/Initialize()
. = ..()
received_table = add_input_port("Input", PORT_TYPE_TABLE)
column_name = add_input_port("Column Name", PORT_TYPE_STRING)
output_list = add_output_port("Output", PORT_TYPE_LIST)
/obj/item/circuit_component/get_column/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/list/input_list = received_table.value
if(!islist(input_list) || isnum(column_name.value))
return
var/list/new_list = list()
for(var/list/entry in input_list)
var/anything = entry[column_name.value]
if(islist(anything))
continue
new_list += anything
output_list.set_output(new_list)
@@ -0,0 +1,42 @@
/**
* # Index Component
*
* Return the index of a list
*/
/obj/item/circuit_component/index
display_name = "Index List"
desc = "A component that returns the value of a list at a given index."
/// The input port
var/datum/port/input/list_port
var/datum/port/input/index_port
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/index/Initialize()
. = ..()
index_port = add_input_port("Index", PORT_TYPE_ANY)
list_port = add_input_port("List", PORT_TYPE_LIST)
output = add_output_port("Value", PORT_TYPE_ANY)
/obj/item/circuit_component/index/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/index = index_port.value
var/list/list_input = list_port.value
if(!islist(list_input) || !index)
output.set_output(null)
return
if(isnum(index) && (index < 1 || index > length(list_input)))
output.set_output(null)
return
output.set_output(list_input[index])
@@ -0,0 +1,42 @@
/**
* # Index Table Component
*
* Gets the row of a table using the index inputted. Will return no value if the index is invalid or a proper table is not returned.
*/
/obj/item/circuit_component/index_table
display_name = "Index Table"
desc = "Gets the row of a table using the index inputted. Will return no value if the index is invalid or a proper table is not returned."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The list to perform the filter on
var/datum/port/input/received_table
/// The target index
var/datum/port/input/target_index
/// The filtered list
var/datum/port/output/output_list
/obj/item/circuit_component/index_table/Initialize()
. = ..()
received_table = add_input_port("Input", PORT_TYPE_TABLE)
target_index = add_input_port("Index", PORT_TYPE_NUMBER)
output_list = add_output_port("Output", PORT_TYPE_LIST)
/obj/item/circuit_component/index_table/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/list/target_list = received_table.value
if(!islist(target_list) || !length(target_list))
output_list.set_output(null)
return
var/index = target_index.value
if(index < 1 || index > length(target_list))
output_list.set_output(null)
return
output_list.set_output(target_list[index])
@@ -0,0 +1,86 @@
/**
* # List Literal Component
*
* Return a list literal.
*/
/obj/item/circuit_component/list_literal
display_name = "List Literal"
desc = "A component that returns the value of a list at a given index. Attack in hand to increase list size, right click to decrease list size."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The inputs used to create the list
var/list/datum/port/input/entry_ports = list()
/// The result from the output
var/datum/port/output/list_output
var/length = 0
var/default_list_size = 2
var/min_size = 1
var/max_size = 20
/obj/item/circuit_component/list_literal/save_data_to_list(list/component_data)
. = ..()
component_data["length"] = length
/obj/item/circuit_component/list_literal/load_data_from_list(list/component_data)
set_list_size(component_data["length"])
return ..()
/obj/item/circuit_component/list_literal/proc/set_list_size(new_size)
if(new_size <= 0)
for(var/datum/port/input/port in entry_ports)
remove_input_port(port)
entry_ports = list()
length = 0
return
while(length > new_size)
var/index = length(entry_ports)
var/entry_port = entry_ports[index]
entry_ports -= entry_port
remove_input_port(entry_port)
length--
while(length < new_size)
length++
var/index = length(input_ports)
if(trigger_input)
index -= 1
entry_ports += add_input_port("Index [index+1]", PORT_TYPE_ANY, index = index+1)
/obj/item/circuit_component/list_literal/Initialize()
. = ..()
set_list_size(default_list_size)
list_output = add_output_port("Value", PORT_TYPE_LIST)
/obj/item/circuit_component/list_literal/Destroy()
list_output = null
return ..()
// Increases list length
/obj/item/circuit_component/list_literal/attack_self(mob/user, list/modifiers)
. = ..()
set_list_size(min(length + 1, max_size))
balloon_alert(user, "new size is now [length]")
// Decreases list length
/obj/item/circuit_component/list_literal/attack_self_secondary(mob/user, list/modifiers)
. = ..()
set_list_size(max(length - 1, min_size))
balloon_alert(user, "new size is now [length]")
/obj/item/circuit_component/list_literal/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/list/new_literal = list()
for(var/datum/port/input/entry_port as anything in entry_ports)
// Prevents lists from merging together
new_literal += list(entry_port.value)
list_output.set_output(new_literal)
@@ -0,0 +1,93 @@
/**
* # Select Component
*
* Selects a list from a list of lists by a specific column. Used only by USBs for communications to and from computers with lists of varying sizes.
*/
/obj/item/circuit_component/select
display_name = "Select Query"
desc = "A component used with USB cables that can perform select queries on a list based on the column name selected. The values are then compared with the comparison input."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/datum/port/input/option/comparison_options
/// The list to perform the filter on
var/datum/port/input/received_table
/// The name of the column to check
var/datum/port/input/column_name
/// The input to compare with
var/datum/port/input/comparison_input
/// The filtered list
var/datum/port/output/filtered_table
var/current_type = PORT_TYPE_ANY
/obj/item/circuit_component/select/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,
)
comparison_options = add_option_port("Comparison Options", component_options)
/obj/item/circuit_component/select/Initialize()
. = ..()
received_table = add_input_port("Input", PORT_TYPE_TABLE)
column_name = add_input_port("Column Name", PORT_TYPE_STRING)
comparison_input = add_input_port("Comparison Input", PORT_TYPE_ANY)
filtered_table = add_output_port("Output", PORT_TYPE_TABLE)
/obj/item/circuit_component/select/input_received(datum/port/input/port)
. = ..()
var/current_option = comparison_options.value
switch(current_option)
if(COMP_COMPARISON_EQUAL, COMP_COMPARISON_NOT_EQUAL)
if(current_type != PORT_TYPE_ANY)
current_type = PORT_TYPE_ANY
comparison_input.set_datatype(PORT_TYPE_ANY)
else
if(current_type != PORT_TYPE_NUMBER)
current_type = PORT_TYPE_NUMBER
comparison_input.set_datatype(PORT_TYPE_NUMBER)
if(.)
return
var/list/input_list = received_table.value
if(!islist(input_list) || isnum(column_name.value))
return
var/comparison_value = comparison_input.value
var/list/new_list = list()
for(var/list/entry in input_list)
var/anything = entry[column_name.value]
if(islist(anything))
continue
if(current_option != COMP_COMPARISON_EQUAL && current_option != COMP_COMPARISON_NOT_EQUAL && !isnum(anything))
continue
var/add_to_list = FALSE
switch(current_option)
if(COMP_COMPARISON_EQUAL)
add_to_list = anything == comparison_value
if(COMP_COMPARISON_NOT_EQUAL)
add_to_list = anything != comparison_value
if(COMP_COMPARISON_GREATER_THAN)
add_to_list = anything > comparison_value
if(COMP_COMPARISON_GREATER_THAN_OR_EQUAL)
add_to_list = anything >= comparison_value
if(COMP_COMPARISON_LESS_THAN)
add_to_list = anything < comparison_value
if(COMP_COMPARISON_LESS_THAN_OR_EQUAL)
add_to_list = anything <= comparison_value
if(add_to_list)
new_list += list(entry)
filtered_table.set_output(new_list)
@@ -0,0 +1,42 @@
/**
* # Split component
*
* Splits a string
*/
/obj/item/circuit_component/split
display_name = "Split"
desc = "Splits a string by the separator, turning it into a list"
/// The input port
var/datum/port/input/input_port
/// The seperator
var/datum/port/input/separator
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/split/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_STRING)
separator = add_input_port("Seperator", PORT_TYPE_STRING)
output = add_output_port("Output", PORT_TYPE_LIST)
/obj/item/circuit_component/split/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/separator_value = separator.value
if(isnull(separator_value))
return
var/value = input_port.value
if(isnull(value))
return
var/list/result = splittext(value,separator_value)
output.set_output(result)
@@ -0,0 +1,88 @@
#define COMP_ARITHMETIC_ADD "Add"
#define COMP_ARITHMETIC_SUBTRACT "Subtract"
#define COMP_ARITHMETIC_MULTIPLY "Multiply"
#define COMP_ARITHMETIC_DIVIDE "Divide"
#define COMP_ARITHMETIC_MIN "Minimum"
#define COMP_ARITHMETIC_MAX "Maximum"
/**
* # Arithmetic Component
*
* General arithmetic unit with add/sub/mult/divide capabilities
* This one only works with numbers.
*/
/obj/item/circuit_component/arithmetic
display_name = "Arithmetic"
desc = "General arithmetic component with arithmetic capabilities."
/// The amount of input ports to have
var/input_port_amount = 4
var/datum/port/input/option/arithmetic_option
/// The result from the output
var/datum/port/output/output
var/list/arithmetic_ports
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/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,
)
arithmetic_option = add_option_port("Arithmetic Option", component_options)
/obj/item/circuit_component/arithmetic/Initialize()
. = ..()
arithmetic_ports = list()
for(var/port_id in 1 to input_port_amount)
var/letter = ascii2text(text2ascii("A") + (port_id-1))
arithmetic_ports += add_input_port(letter, PORT_TYPE_NUMBER)
output = add_output_port("Output", PORT_TYPE_NUMBER)
/obj/item/circuit_component/arithmetic/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/list/ports = arithmetic_ports.Copy()
var/datum/port/input/first_port = popleft(ports)
var/result = first_port.value
for(var/datum/port/input/input_port as anything in ports)
var/value = input_port.value
if(isnull(value))
continue
switch(arithmetic_option.value)
if(COMP_ARITHMETIC_ADD)
result += value
if(COMP_ARITHMETIC_SUBTRACT)
result -= value
if(COMP_ARITHMETIC_MULTIPLY)
result *= value
if(COMP_ARITHMETIC_DIVIDE)
// Protect from div by zero errors.
if(value == 0)
result = null
break
result /= value
if(COMP_ARITHMETIC_MAX)
result = max(result, value)
if(COMP_ARITHMETIC_MIN)
result = min(result, value)
output.set_output(result)
#undef COMP_ARITHMETIC_ADD
#undef COMP_ARITHMETIC_SUBTRACT
#undef COMP_ARITHMETIC_MULTIPLY
#undef COMP_ARITHMETIC_DIVIDE
#undef COMP_ARITHMETIC_MIN
#undef COMP_ARITHMETIC_MAX
@@ -0,0 +1,62 @@
/**
* # Comparison Component
*
* Compares two objects
*/
/obj/item/circuit_component/compare/comparison
display_name = "Comparison"
desc = "A component that compares two objects."
var/datum/port/input/option/comparison_option
input_port_amount = 2
var/current_type = PORT_TYPE_ANY
/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,
)
comparison_option = add_option_port("Comparison Option", component_options)
/obj/item/circuit_component/compare/comparison/input_received(datum/port/input/port)
switch(comparison_option.value)
if(COMP_COMPARISON_EQUAL, COMP_COMPARISON_NOT_EQUAL)
if(current_type != PORT_TYPE_ANY)
current_type = PORT_TYPE_ANY
compare_ports[1].set_datatype(PORT_TYPE_ANY)
compare_ports[2].set_datatype(PORT_TYPE_ANY)
else
if(current_type != PORT_TYPE_NUMBER)
current_type = PORT_TYPE_NUMBER
compare_ports[1].set_datatype(PORT_TYPE_NUMBER)
compare_ports[2].set_datatype(PORT_TYPE_NUMBER)
return ..()
/obj/item/circuit_component/compare/comparison/do_comparisons(list/ports)
if(length(ports) < input_port_amount)
return FALSE
// Comparison component only compares the first two ports
var/input1 = compare_ports[1].value
var/input2 = compare_ports[2].value
var/current_option = comparison_option.value
switch(current_option)
if(COMP_COMPARISON_EQUAL)
return input1 == input2
if(COMP_COMPARISON_NOT_EQUAL)
return input1 != input2
if(COMP_COMPARISON_GREATER_THAN)
return input1 > input2
if(COMP_COMPARISON_GREATER_THAN_OR_EQUAL)
return input1 >= input2
if(COMP_COMPARISON_LESS_THAN)
return input1 < input2
if(COMP_COMPARISON_LESS_THAN_OR_EQUAL)
return input1 <= input2
@@ -0,0 +1,29 @@
/**
* # Length Component
*
* Return the length of an input
*/
/obj/item/circuit_component/length
display_name = "Length"
desc = "A component that returns the length of its input."
/// The input port
var/datum/port/input/input_port
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/length/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_ANY)
output = add_output_port("Length", PORT_TYPE_NUMBER)
/obj/item/circuit_component/length/input_received(datum/port/input/port)
. = ..()
if(.)
return
output.set_output(length(input_port.value))
@@ -0,0 +1,57 @@
#define COMP_LOGIC_AND "AND"
#define COMP_LOGIC_OR "OR"
#define COMP_LOGIC_XOR "XOR"
/**
* # Logic Component
*
* General logic unit with AND OR capabilities
*/
/obj/item/circuit_component/compare/logic
display_name = "Logic"
desc = "A component with 'and' and 'or' capabilities."
var/datum/port/input/option/logic_options
/obj/item/circuit_component/compare/logic/populate_options()
var/static/component_options = list(
COMP_LOGIC_AND,
COMP_LOGIC_OR,
COMP_LOGIC_XOR,
)
logic_options = add_option_port("Logic Options", component_options)
/obj/item/circuit_component/compare/logic/do_comparisons(list/ports)
. = FALSE
var/current_option = logic_options.value
// Used by XOR
var/total_ports = 0
var/total_true_ports = 0
for(var/datum/port/input/port as anything in ports)
if(isnull(port.value) && length(port.connected_ports) == 0)
continue
total_ports += 1
switch(current_option)
if(COMP_LOGIC_AND)
if(!port.value)
return FALSE
. = TRUE
if(COMP_LOGIC_OR)
if(port.value)
return TRUE
if(COMP_LOGIC_XOR)
if(port.value)
. = TRUE
total_true_ports += 1
if(current_option == COMP_LOGIC_XOR)
if(total_ports == total_true_ports)
return FALSE
if(.)
return TRUE
#undef COMP_LOGIC_AND
#undef COMP_LOGIC_OR
#undef COMP_LOGIC_XOR
@@ -0,0 +1,29 @@
/**
* # Logic Component
*
* General logic unit with AND OR capabilities
*/
/obj/item/circuit_component/not
display_name = "Not"
desc = "A component that inverts its input."
/// The input port
var/datum/port/input/input_port
/// The result from the output
var/datum/port/output/result
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/not/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_ANY)
result = add_output_port("Result", PORT_TYPE_NUMBER)
/obj/item/circuit_component/not/input_received(datum/port/input/port)
. = ..()
if(.)
return
result.set_output(!input_port.value)
@@ -0,0 +1,40 @@
/**
* # Random Component
*
* Generates a random number between specific values
*/
/obj/item/circuit_component/random
display_name = "Random"
desc = "A component that returns random values."
/// The minimum value that the random number can be
var/datum/port/input/minimum
/// The maximum value that the random number can be
var/datum/port/input/maximum
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The result from the output
var/datum/port/output/output
/obj/item/circuit_component/random/Initialize()
. = ..()
minimum = add_input_port("Minimum", PORT_TYPE_NUMBER, FALSE)
maximum = add_input_port("Maximum", PORT_TYPE_NUMBER, FALSE)
output = add_output_port("Output", PORT_TYPE_NUMBER)
/obj/item/circuit_component/random/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/min_val = minimum.value || 0
var/max_val = maximum.value || 0
if(max_val < min_val)
output.set_output(0)
return
output.set_output(rand(min_val, max_val))
@@ -0,0 +1,62 @@
/**
* # NTNet Receiver Component
*
* Receives data through NTNet.
*/
/obj/item/circuit_component/ntnet_receive
display_name = "NTNet Receiver"
desc = "Receives data packages through NTNet. If Encryption Key is set then only signals with the same Encryption Key will be received."
circuit_flags = CIRCUIT_FLAG_OUTPUT_SIGNAL //trigger_output
network_id = __NETWORK_CIRCUITS
var/datum/port/input/push_hid
var/datum/port/output/hid
var/datum/port/output/data_package
var/datum/port/output/secondary_package
var/datum/port/input/enc_key
var/datum/port/input/option/data_type_options
var/datum/port/input/option/secondary_data_type_options
/obj/item/circuit_component/ntnet_receive/Initialize()
. = ..()
data_package = add_output_port("Data Package", PORT_TYPE_ANY)
secondary_package = add_output_port("Secondary Package", PORT_TYPE_ANY)
enc_key = add_input_port("Encryption Key", PORT_TYPE_STRING)
RegisterSignal(src, COMSIG_COMPONENT_NTNET_RECEIVE, .proc/ntnet_receive)
/obj/item/circuit_component/ntnet_receive/populate_options()
var/static/component_options = list(
PORT_TYPE_ANY,
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
)
data_type_options = add_option_port("Data Type", component_options)
secondary_data_type_options = add_option_port("Secondary Data Type", component_options)
/obj/item/circuit_component/ntnet_receive/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(COMPONENT_TRIGGERED_BY(data_type_options, port))
data_package.set_datatype(data_type_options.value)
if(COMPONENT_TRIGGERED_BY(secondary_data_type_options, port))
secondary_package.set_datatype(secondary_data_type_options.value)
return TRUE
/obj/item/circuit_component/ntnet_receive/proc/ntnet_receive(datum/source, datum/netdata/data)
SIGNAL_HANDLER
if(data.data["enc_key"] != enc_key.value)
return
data_package.set_output(data.data["data"])
secondary_package.set_output(data.data["data_secondary"])
trigger_output.set_output(COMPONENT_SIGNAL)
@@ -0,0 +1,29 @@
/**
* # NTNet Transmitter Component
*
* Sends a data package through NTNet
*/
/obj/item/circuit_component/ntnet_send
display_name = "NTNet Transmitter"
desc = "Sends a data package through NTNet. If Encryption Key is set then transmitted data will be only picked up by receivers with the same Encryption Key."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL
network_id = __NETWORK_CIRCUITS
var/datum/port/input/data_package
var/datum/port/input/secondary_package
var/datum/port/input/enc_key
/obj/item/circuit_component/ntnet_send/Initialize()
. = ..()
data_package = add_input_port("Data Package", PORT_TYPE_ANY)
secondary_package = add_input_port("Secondary Package", PORT_TYPE_ANY)
enc_key = add_input_port("Encryption Key", PORT_TYPE_STRING)
/obj/item/circuit_component/ntnet_send/input_received(datum/port/input/port)
. = ..()
if(.)
return
ntnet_send(list("data" = data_package.value, "data_secondary" = secondary_package.value, "enc_key" = enc_key.value))
@@ -0,0 +1,36 @@
/**
* # Pressure Sensor
*
* Returns the pressure of the tile
*/
/obj/item/circuit_component/pressuresensor
display_name = "Pressure Sensor"
desc = "Outputs the current pressure of the tile"
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The result from the output
var/datum/port/output/result
/obj/item/circuit_component/pressuresensor/Initialize()
. = ..()
result = add_output_port("Result", PORT_TYPE_NUMBER)
/obj/item/circuit_component/pressuresensor/input_received(datum/port/input/port)
. = ..()
if(.)
return
//Get current turf
var/turf/location = get_turf(src)
if(!location)
result.set_output(null)
return
//Get environment info
var/datum/gas_mixture/environment = location.return_air()
var/total_moles = environment.total_moles()
var/pressure = environment.return_pressure()
if(total_moles)
//If there's atmos, return pressure
result.set_output(round(pressure,1))
else
result.set_output(0)
@@ -0,0 +1,36 @@
/**
* # Temperature Sensor
*
* Returns the temperature of the tile
*/
/obj/item/circuit_component/tempsensor
display_name = "Temperature Sensor"
desc = "Outputs the current temperature of the tile"
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The result from the output
var/datum/port/output/result
/obj/item/circuit_component/tempsensor/Initialize()
. = ..()
result = add_output_port("Result", PORT_TYPE_NUMBER)
/obj/item/circuit_component/tempsensor/input_received(datum/port/input/port)
. = ..()
if(.)
return
//Get current turf
var/turf/location = get_turf(src)
if(!location)
result.set_output(null)
return
//Get environment info
var/datum/gas_mixture/environment = location.return_air()
var/total_moles = environment.total_moles()
if(total_moles)
//If there's atmos, return temperature
result.set_output(round(environment.temperature,1))
else
result.set_output(0)
@@ -0,0 +1,42 @@
/**
* # Concatenate Component
*
* General string concatenation component. Puts strings together.
*/
/obj/item/circuit_component/concat
display_name = "Concatenate"
desc = "A component that combines strings."
/// The amount of input ports to have
var/input_port_amount = 4
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/concat/Initialize()
. = ..()
for(var/port_id in 1 to input_port_amount)
var/letter = ascii2text(text2ascii("A") + (port_id-1))
add_input_port(letter, PORT_TYPE_STRING)
output = add_output_port("Output", PORT_TYPE_STRING)
/obj/item/circuit_component/concat/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/result = ""
var/list/ports = input_ports.Copy()
ports -= trigger_input
for(var/datum/port/input/input_port as anything in ports)
var/value = input_port.value
if(isnull(value))
continue
result += "[value]"
output.set_output(result)
@@ -0,0 +1,35 @@
/**
* # String Contains Component
*
* Checks if a string contains a word/letter
*/
/obj/item/circuit_component/compare/contains
display_name = "String Contains"
desc = "Checks if a string contains a word/letter"
input_port_amount = 0
var/datum/port/input/needle
var/datum/port/input/haystack
/obj/item/circuit_component/compare/contains/load_custom_ports()
needle = add_input_port("Needle", PORT_TYPE_STRING)
haystack = add_input_port("Haystack", PORT_TYPE_STRING)
/obj/item/circuit_component/compare/contains/Destroy()
needle = null
haystack = null
return ..()
/obj/item/circuit_component/compare/contains/do_comparisons(list/ports)
if(length(ports) < input_port_amount)
return
var/to_find = needle.value
var/to_search = haystack.value
if(!to_find || !to_search)
return
return findtext(to_search, to_find)
@@ -0,0 +1,54 @@
#define COMP_TEXT_LOWER "To Lower"
#define COMP_TEXT_UPPER "To Upper"
/**
* # Text Component
*
* Either makes the text upper case or lower case.
*/
/obj/item/circuit_component/textcase
display_name = "Text Case"
desc = "A component that makes its input uppercase or lowercase."
var/datum/port/input/option/textcase_options
/// The input port
var/datum/port/input/input_port
/// 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/textcase/populate_options()
var/static/component_options = list(
COMP_TEXT_LOWER,
COMP_TEXT_UPPER,
)
textcase_options = add_option_port("Textcase Options", component_options)
/obj/item/circuit_component/textcase/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_STRING)
output = add_output_port("Output", PORT_TYPE_STRING)
/obj/item/circuit_component/textcase/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/value = input_port.value
if(isnull(value))
return
var/result
switch(textcase_options.value)
if(COMP_TEXT_LOWER)
result = lowertext(value)
if(COMP_TEXT_UPPER)
result = uppertext(value)
output.set_output(result)
#undef COMP_TEXT_LOWER
#undef COMP_TEXT_UPPER
@@ -0,0 +1,29 @@
/**
* #To Number Component
*
* Converts a string into a Number
*/
/obj/item/circuit_component/tonumber
display_name = "To Number"
desc = "A component that converts its input (a string) to a number. If there's text in the input, it'll only consider it if it starts with a number. It will take that number and ignore the rest."
/// The input port
var/datum/port/input/input_port
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/obj/item/circuit_component/tonumber/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_STRING)
output = add_output_port("Output", PORT_TYPE_NUMBER)
/obj/item/circuit_component/tonumber/input_received(datum/port/input/port)
. = ..()
if(.)
return
output.set_output(text2num(input_port.value))
@@ -0,0 +1,40 @@
/**
* # To String Component
*
* Converts any value into a string
*/
/obj/item/circuit_component/tostring
display_name = "To String"
desc = "A component that converts its input to text."
/// The input port
var/datum/port/input/input_port
/// The result from the output
var/datum/port/output/output
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/max_range = 5
/obj/item/circuit_component/tostring/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_ANY)
output = add_output_port("Output", PORT_TYPE_STRING)
/obj/item/circuit_component/tostring/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/value = input_port.value
if(isatom(value))
var/turf/location = get_turf(src)
var/atom/object = value
if(object.z != location.z || get_dist(location, object) > max_range)
output.set_output(PORT_TYPE_ATOM)
return
output.set_output("[value]")
@@ -0,0 +1,57 @@
/**
* # Clock Component
*
* Fires every tick of the circuit timer SS
*/
/obj/item/circuit_component/clock
display_name = "Clock"
desc = "A component that repeatedly fires."
/// Whether the clock is on or not
var/datum/port/input/on
/// The signal from this clock component
var/datum/port/output/signal
/obj/item/circuit_component/clock/get_ui_notices()
. = ..()
. += create_ui_notice("Clock Interval: [DisplayTimeText(COMP_CLOCK_DELAY)]", "orange", "clock")
/obj/item/circuit_component/clock/Initialize()
. = ..()
on = add_input_port("On", PORT_TYPE_NUMBER)
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/clock/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(on.value)
start_process()
else
stop_process()
/obj/item/circuit_component/clock/Destroy()
stop_process()
return ..()
/obj/item/circuit_component/clock/process(delta_time)
signal.set_output(COMPONENT_SIGNAL)
/**
* Adds the component to the SSclock_component process list
*
* Starts ticking to send signals between periods of time
*/
/obj/item/circuit_component/clock/proc/start_process()
START_PROCESSING(SSclock_component, src)
/**
* Removes the component to the SSclock_component process list
*
* Signals stop getting sent.
*/
/obj/item/circuit_component/clock/proc/stop_process()
STOP_PROCESSING(SSclock_component, src)
@@ -0,0 +1,43 @@
/// The minimum delay value that the delay component can have.
#define COMP_DELAY_MIN_VALUE 0.1
/**
* # Delay Component
*
* Delays a signal by a specified duration.
*/
/obj/item/circuit_component/delay
display_name = "Delay"
desc = "A component that delays a signal by a specified duration."
/// Amount to delay by
var/datum/port/input/delay_amount
/// Input signal to fire the delay
var/datum/port/input/trigger
/// The output of the signal
var/datum/port/output/output
/obj/item/circuit_component/delay/Initialize()
. = ..()
delay_amount = add_input_port("Delay", PORT_TYPE_NUMBER, FALSE)
trigger = add_input_port("Trigger", PORT_TYPE_SIGNAL)
output = add_output_port("Result", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/delay/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(!COMPONENT_TRIGGERED_BY(trigger, port))
return
var/delay = delay_amount.value
if(delay > COMP_DELAY_MIN_VALUE)
// Convert delay into deciseconds
addtimer(CALLBACK(output, /datum/port/output.proc/set_output, trigger.value), delay*10)
else
output.set_output(trigger.value)
#undef COMP_DELAY_MIN_VALUE
@@ -0,0 +1,70 @@
/**
* # Getter Component
*
* Gets the current value from a variable.
*/
/obj/item/circuit_component/getter
display_name = "Variable Getter"
desc = "A component that gets a variable globally on the circuit."
/// Variable name
var/datum/port/input/option/variable_name
/// The value of the variable
var/datum/port/output/value
var/datum/circuit_variable/current_variable
/obj/item/circuit_component/getter/populate_options()
variable_name = add_option_port("Variable", null)
/obj/item/circuit_component/getter/add_to(obj/item/integrated_circuit/added_to)
. = ..()
variable_name.possible_options = added_to.circuit_variables
/obj/item/circuit_component/getter/removed_from(obj/item/integrated_circuit/removed_from)
variable_name.possible_options = null
return ..()
/obj/item/circuit_component/getter/Initialize()
. = ..()
value = add_output_port("Value", PORT_TYPE_ANY)
/obj/item/circuit_component/getter/input_received(datum/port/input/port)
. = ..()
// We don't care much about the parent's return value. We only care if the parent exists
// since this should never really fail.
if(!parent)
return
var/variable_string = variable_name.value
if(!variable_string)
remove_current_variable()
value.set_output(null)
return
var/datum/circuit_variable/variable = parent.circuit_variables[variable_string]
if(!variable)
remove_current_variable()
value.set_output(null)
return
set_current_variable(variable)
value.set_output(variable.value)
/obj/item/circuit_component/getter/proc/remove_current_variable()
SIGNAL_HANDLER
if(current_variable)
current_variable.remove_listener(src)
UnregisterSignal(current_variable, COMSIG_PARENT_QDELETING)
current_variable = null
/obj/item/circuit_component/getter/proc/set_current_variable(datum/circuit_variable/variable)
if(variable == current_variable)
return
remove_current_variable()
current_variable = variable
current_variable.add_listener(src)
RegisterSignal(current_variable, COMSIG_PARENT_QDELETING, .proc/remove_current_variable)
value.set_datatype(variable.datatype)
@@ -0,0 +1,82 @@
/**
* # Router Component
*
* Writes one of multiple inputs to one of multiple outputs.
*/
/obj/item/circuit_component/router
display_name = "Router"
desc = "Copies the input chosen by \"Input Selector\" to the output chosen by \"Output Selector\"."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/datum/port/input/option/router_options
/// Which ports to connect.
var/datum/port/input/input_selector
var/datum/port/input/output_selector
/// How many ports to have.
var/input_port_amount = 4
var/output_port_amount = 4
/// Current type of the ports
var/current_type
/// The ports to route.
var/list/datum/port/input/ins
var/list/datum/port/output/outs
/obj/item/circuit_component/router/populate_options()
var/static/component_options = list(
PORT_TYPE_ANY,
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
)
router_options = add_option_port("Router Options", component_options)
/obj/item/circuit_component/router/Initialize()
. = ..()
current_type = router_options.value
if(input_port_amount > 1)
input_selector = add_input_port("Input Selector", PORT_TYPE_NUMBER, default = 1)
if(output_port_amount > 1)
output_selector = add_input_port("Output Selector", PORT_TYPE_NUMBER, default = 1)
ins = list()
for(var/port_id in 1 to input_port_amount)
ins += add_input_port(input_port_amount > 1 ? "Input [port_id]" : "Input", current_type)
outs = list()
for(var/port_id in 1 to output_port_amount)
outs += add_output_port(output_port_amount > 1 ? "Output [port_id]" : "Output", current_type)
/obj/item/circuit_component/router/Destroy()
input_selector = null
output_selector = null
ins.Cut()
ins = null
outs.Cut()
outs = null
return ..()
// If I is in range, L[I]. If I is out of range, wrap around.
#define WRAPACCESS(L, I) L[(((I||1)-1)%length(L)+length(L))%length(L)+1]
/obj/item/circuit_component/router/input_received(datum/port/input/port)
. = ..()
var/current_option = router_options.value
if(current_type != current_option)
current_type = current_option
for(var/datum/port/input/input as anything in ins)
input.set_datatype(current_type)
for(var/datum/port/output/output as anything in outs)
output.set_datatype(current_type)
if(.)
return
var/datum/port/input/input = WRAPACCESS(ins, input_selector ? input_selector.value : 1)
var/datum/port/output/output = WRAPACCESS(outs, output_selector ? output_selector.value : 1)
output.set_output(input.value)
/obj/item/circuit_component/router/multiplexer
display_name = "Multiplexer"
desc = "Copies the input chosen by \"Input Selector\" to the output."
output_port_amount = 1
@@ -0,0 +1,58 @@
/**
* # Setter Component
*
* Stores the current input when triggered into a variable.
*/
/obj/item/circuit_component/setter
display_name = "Variable Setter"
desc = "A component that sets a variable globally on the circuit."
circuit_flags = CIRCUIT_FLAG_OUTPUT_SIGNAL
/// Variable name
var/datum/port/input/option/variable_name
/// The input to store
var/datum/port/input/input_port
/// The trigger to store the current value of the input
var/datum/port/input/trigger
var/current_type
/obj/item/circuit_component/setter/populate_options()
variable_name = add_option_port("Variable", null)
/obj/item/circuit_component/setter/add_to(obj/item/integrated_circuit/added_to)
. = ..()
variable_name.possible_options = added_to.circuit_variables
/obj/item/circuit_component/setter/removed_from(obj/item/integrated_circuit/removed_from)
variable_name.possible_options = null
return ..()
/obj/item/circuit_component/setter/Initialize()
. = ..()
input_port = add_input_port("Input", PORT_TYPE_ANY)
trigger = add_input_port("Store", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/setter/input_received(datum/port/input/port)
. = ..()
var/variable_string = variable_name.value
if(!variable_string)
return
var/datum/circuit_variable/variable = parent.circuit_variables[variable_string]
if(!variable)
return
if(variable.datatype != current_type)
current_type = variable.datatype
input_port.set_datatype(current_type)
if(.)
return
if(!COMPONENT_TRIGGERED_BY(trigger, port))
return TRUE
variable.set_value(input_port.value)
@@ -0,0 +1,60 @@
/**
* # Typecast Component
*
* A component that casts a value to a type if it matches or outputs null.
*/
/obj/item/circuit_component/typecast
display_name = "Typecast"
desc = "A component that casts a value to a type if it matches or outputs null."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
var/datum/port/input/option/typecast_options
var/datum/port/input/input_value
var/datum/port/output/output_value
var/current_type
/obj/item/circuit_component/typecast/Initialize()
. = ..()
current_type = typecast_options.value
input_value = add_input_port("Input", PORT_TYPE_ANY)
output_value = add_output_port("Output", current_type)
/obj/item/circuit_component/typecast/populate_options()
var/static/list/component_options = list(
PORT_TYPE_STRING,
PORT_TYPE_NUMBER,
PORT_TYPE_LIST,
PORT_TYPE_ATOM,
)
typecast_options = add_option_port("Typecast Options", component_options)
/obj/item/circuit_component/typecast/input_received(datum/port/input/port)
. = ..()
var/current_option = typecast_options.value
if(current_type != current_option)
current_type = current_option
output_value.set_datatype(current_type)
if(.)
return
var/value = input_value.value
var/value_to_set = null
switch(current_option)
if(PORT_TYPE_STRING)
if(istext(value))
value_to_set = value
if(PORT_TYPE_NUMBER)
if(isnum(value))
value_to_set = value
if(PORT_TYPE_LIST)
if(islist(value))
value_to_set = value
if(PORT_TYPE_ATOM)
if(isatom(value))
value_to_set = value
output_value.set_output(value_to_set)
@@ -0,0 +1,51 @@
#define COMP_TYPECHECK_MOB "organism"
#define COMP_TYPECHECK_HUMAN "humanoid"
/**
* # Typecheck Component
*
* Checks the type of a value
*/
/obj/item/circuit_component/compare/typecheck
display_name = "Typecheck"
desc = "A component that checks the type of its input."
input_port_amount = 1
var/datum/port/input/option/typecheck_options
/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,
)
typecheck_options = add_option_port("Typecheck Options", component_options)
/obj/item/circuit_component/compare/typecheck/do_comparisons(list/ports)
if(!length(ports))
return
. = FALSE
// We're only comparing the first port/value. There shouldn't be any more.
var/datum/port/input/input_port = ports[1]
var/input_val = input_port.value
switch(typecheck_options.value)
if(PORT_TYPE_STRING)
return istext(input_val)
if(PORT_TYPE_NUMBER)
return isnum(input_val)
if(PORT_TYPE_LIST)
return islist(input_val)
if(PORT_TYPE_ATOM)
return isatom(input_val)
if(COMP_TYPECHECK_MOB)
return ismob(input_val)
if(COMP_TYPECHECK_HUMAN)
return ishuman(input_val)
#undef COMP_TYPECHECK_MOB
#undef COMP_TYPECHECK_HUMAN