Files
S.P.L.U.R.T-Station-13/code/modules/wiremod/core/port.dm
2021-10-28 12:51:31 +08:00

217 lines
6.4 KiB
Plaintext

/**
* # Component Port
*
* A port used by a component. Connects to other ports.
*/
/datum/port
/// The component this port is attached to
var/obj/item/circuit_component/connected_component
/// Name of the port. Used when displaying the port.
var/name
/// The port type. Ports can only connect to each other if the type matches
var/datatype
/// The value that's currently in the port. It's of the above type.
var/value
/// The default port type. Stores the original datatype of the port set on Initialize.
var/datum/circuit_datatype/datatype_handler
/// The port color. If unset, appears as blue.
var/color
/datum/port/New(obj/item/circuit_component/to_connect, name, datatype)
if(!to_connect)
qdel(src)
return
. = ..()
connected_component = to_connect
src.name = name
set_datatype(datatype)
/datum/port/Destroy(force)
disconnect_all()
connected_component = null
datatype_handler = null
return ..()
/**
* Sets the port's value to value.
* Casts to the port's datatype (e.g. number -> string), and assumes this can be done.
*/
/datum/port/proc/set_value(value, force = FALSE)
if(src.value != value || force)
if(isatom(value))
UnregisterSignal(value, COMSIG_PARENT_QDELETING)
src.value = datatype_handler.convert_value(src, value)
if(isatom(value))
RegisterSignal(value, COMSIG_PARENT_QDELETING, .proc/null_value)
SEND_SIGNAL(src, COMSIG_PORT_SET_VALUE, value)
/**
* Updates the value of the input and calls input_received on the connected component
*/
/datum/port/input/proc/set_input(value)
if(QDELETED(src)) //Pain
return
set_value(value)
if(trigger)
TRIGGER_CIRCUIT_COMPONENT(connected_component, src)
/datum/port/output/proc/set_output(value)
set_value(value)
/**
* Sets the datatype of the port.
*
* Arguments:
* * new_type - The type this port is to be set to.
*/
/datum/port/proc/set_datatype(type_to_set)
if(type_to_set == datatype)
return
if(datatype_handler)
datatype_handler.on_loss(src)
datatype_handler = null
var/datum/circuit_datatype/handler = GLOB.circuit_datatypes[type_to_set]
if(!handler || !handler.is_compatible(src))
type_to_set = PORT_TYPE_ANY
handler = GLOB.circuit_datatypes[type_to_set]
// We can't leave this port without a type or else it'll just keep spewing out unnecessary and unneeded runtimes as well as leaving the circuit in a broken state.
stack_trace("[src] port attempted to be set to an incompatible datatype! (target datatype to set: [type_to_set])")
datatype = type_to_set
datatype_handler = handler
color = datatype_handler.color
datatype_handler.on_gain(src)
src.value = datatype_handler.convert_value(src, value)
SEND_SIGNAL(src, COMSIG_PORT_SET_TYPE, type_to_set)
if(connected_component?.parent)
SStgui.update_uis(connected_component.parent)
/datum/port/input/set_datatype(new_type)
for(var/datum/port/output/output as anything in connected_ports)
check_type(output)
..()
/**
* Returns the data from the datatype
*/
/datum/port/proc/datatype_ui_data()
return datatype_handler.datatype_ui_data(src)
/**
* # Output Port
*
* An output port that many input ports can connect to
*
* Sends a signal whenever the output value is changed
*/
/datum/port/output
/**
* Disconnects a port from all other ports.
*
* Called by [/obj/item/circuit_component] whenever it is disconnected from
* an integrated circuit
*/
/datum/port/proc/disconnect_all()
SEND_SIGNAL(src, COMSIG_PORT_DISCONNECT)
/datum/port/input/disconnect_all()
..()
for(var/datum/port/output/output as anything in connected_ports)
disconnect(output)
/datum/port/input/proc/disconnect(datum/port/output/output)
connected_ports -= output
UnregisterSignal(output, COMSIG_PORT_SET_VALUE)
UnregisterSignal(output, COMSIG_PORT_SET_TYPE)
UnregisterSignal(output, COMSIG_PORT_DISCONNECT)
/// Do our part in setting all source references anywhere to null.
/datum/port/proc/on_value_qdeleting(datum/source)
SIGNAL_HANDLER
if(value == source)
value = null
else
stack_trace("Impossible? [src] should only receive COMSIG_PARENT_QDELETING from an atom currently in the port, not [source].")
/**
* # Input Port
*
* An input port remembers connected output ports.
*
* Registers the PORT_SET_VALUE signal on each connected port,
* and keeps its value equal to the last such signal received.
*/
/datum/port/input
/// Whether this port triggers an update whenever an output is received.
var/trigger = FALSE
/// The ports this port is wired to.
var/list/datum/port/output/connected_ports
/datum/port/input/New(obj/item/circuit_component/to_connect, name, datatype, trigger, default)
. = ..()
set_value(default)
src.trigger = trigger
src.connected_ports = list()
/**
* Introduces two ports to one another.
*/
/datum/port/input/proc/connect(datum/port/output/output)
connected_ports |= output
RegisterSignal(output, COMSIG_PORT_SET_VALUE, .proc/receive_value)
RegisterSignal(output, COMSIG_PORT_SET_TYPE, .proc/check_type)
RegisterSignal(output, COMSIG_PORT_DISCONNECT, .proc/disconnect)
// For signals, we don't update the input to prevent sending a signal when connecting ports.
if(!(datatype_handler.datatype_flags & DATATYPE_FLAG_AVOID_VALUE_UPDATE))
set_input(output.value)
/**
* Determines if a datatype is compatible with another port of a different type.
*
* Arguments:
* * other_datatype - The datatype to check
*/
/datum/port/input/proc/can_receive_from_datatype(datatype_to_check)
return datatype_handler.can_receive_from_datatype(datatype_to_check)
/**
* Determines if a datatype is compatible with another port of a different type.
*
* Arguments:
* * other_datatype - The datatype to check
*/
/datum/port/input/proc/handle_manual_input(mob/user, manual_input)
if(datatype_handler.datatype_flags & DATATYPE_FLAG_ALLOW_MANUAL_INPUT)
return datatype_handler.handle_manual_input(src, user, manual_input)
return null
/**
* Mirror value updates from connected output ports after an input_receive_delay.
*/
/datum/port/input/proc/receive_value(datum/port/output/output, value)
SIGNAL_HANDLER
SScircuit_component.add_callback(CALLBACK(src, .proc/set_input, value))
/// Signal handler proc to null the input if an atom is deleted. An update is not sent because this was not set by anything.
/datum/port/proc/null_value(datum/source)
SIGNAL_HANDLER
if(value == source)
value = null
/**
* Handle type updates from connected output ports, breaking uncastable connections.
*/
/datum/port/input/proc/check_type(datum/port/output/output)
SIGNAL_HANDLER
if(!can_receive_from_datatype(output.datatype))
disconnect(output)