tgui backend
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,75 @@
|
||||
/// An admin verb to view all circuits, plus useful information
|
||||
/datum/admins/proc/view_all_circuits()
|
||||
set category = "Admin.Game"
|
||||
set name = "View All Circuits"
|
||||
|
||||
var/static/datum/circuit_admin_panel/circuit_admin_panel = new
|
||||
circuit_admin_panel.ui_interact(usr)
|
||||
|
||||
/datum/circuit_admin_panel
|
||||
|
||||
/datum/circuit_admin_panel/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["circuits"] = list()
|
||||
|
||||
for (var/obj/item/integrated_circuit/circuit as anything in GLOB.integrated_circuits)
|
||||
var/datum/mind/inserter = circuit.inserter_mind?.resolve()
|
||||
|
||||
data["circuits"] += list(list(
|
||||
"ref" = REF(circuit),
|
||||
"name" = "[circuit.name] in [loc_name(circuit)]",
|
||||
"creator" = circuit.get_creator(),
|
||||
"has_inserter" = !isnull(inserter),
|
||||
))
|
||||
|
||||
return data
|
||||
|
||||
/datum/circuit_admin_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return .
|
||||
|
||||
if (!istext(params["circuit"]))
|
||||
return FALSE
|
||||
|
||||
var/obj/item/integrated_circuit/circuit = locate(params["circuit"])
|
||||
if (!istype(circuit))
|
||||
to_chat(usr, span_warning("That circuit no longer exists."))
|
||||
return FALSE
|
||||
|
||||
switch (action)
|
||||
if ("duplicate_circuit")
|
||||
if (alert(usr, "This will spawn the new circuit at where you are, are you sure?", "Confirm", "Yes", "No") != "Yes")
|
||||
return FALSE
|
||||
|
||||
var/list/errors = list()
|
||||
|
||||
var/obj/item/integrated_circuit/loaded/new_circuit = new(usr.drop_location())
|
||||
new_circuit.load_circuit_data(circuit.convert_to_json(), errors)
|
||||
|
||||
if (length(errors))
|
||||
to_chat(usr, span_warning("Somehow, duplicating the circuit failed:"))
|
||||
for (var/error in errors)
|
||||
to_chat(usr, span_warning(error))
|
||||
if ("follow_circuit")
|
||||
usr.client?.admin_follow(circuit)
|
||||
if ("save_circuit")
|
||||
circuit.attempt_save_to(usr.client)
|
||||
if ("vv_circuit")
|
||||
usr.client?.debug_variables(circuit)
|
||||
if ("open_circuit")
|
||||
circuit.ui_interact(usr)
|
||||
if ("open_player_panel")
|
||||
var/datum/mind/inserter = circuit.inserter_mind?.resolve()
|
||||
usr.client?.holder?.show_player_panel(inserter?.current)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/circuit_admin_panel/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/circuit_admin_panel/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "CircuitAdminPanel")
|
||||
ui.open()
|
||||
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* # Integrated Circuit Component
|
||||
*
|
||||
* A component that performs a function when given an input
|
||||
*
|
||||
* Can be attached to an integrated circuitboard, where it can then
|
||||
* be connected between other components to provide an output or to receive
|
||||
* an input. This is the base type of all components
|
||||
*/
|
||||
/obj/item/circuit_component
|
||||
name = COMPONENT_DEFAULT_NAME
|
||||
icon = 'icons/obj/module.dmi'
|
||||
icon_state = "component"
|
||||
inhand_icon_state = "electronic"
|
||||
|
||||
/// The name of the component shown on the UI
|
||||
var/display_name = "Generic"
|
||||
|
||||
/// The integrated_circuit that this component is attached to.
|
||||
var/obj/item/integrated_circuit/parent
|
||||
|
||||
/// A list that contains the outpurt ports on this component
|
||||
/// Used to connect between the ports
|
||||
var/list/datum/port/output/output_ports = list()
|
||||
|
||||
/// A list that contains the components the input ports on this component
|
||||
/// Used to connect between the ports
|
||||
var/list/datum/port/input/input_ports = list()
|
||||
|
||||
/// Generic trigger input for triggering this component
|
||||
var/datum/port/input/trigger_input
|
||||
var/datum/port/output/trigger_output
|
||||
|
||||
/// The flags of the circuit to control basic generalised behaviour.
|
||||
var/circuit_flags = NONE
|
||||
|
||||
/// Used to determine the x position of the component within the UI
|
||||
var/rel_x = 0
|
||||
/// Used to determine the y position of the component within the UI
|
||||
var/rel_y = 0
|
||||
|
||||
/// The power usage whenever this component receives an input
|
||||
var/power_usage_per_input = 1
|
||||
|
||||
// Whether the component is removable or not. Only affects user UI
|
||||
var/removable = TRUE
|
||||
|
||||
// Defines which shells support this component. Only used as an informational guide, does not restrict placing these components in circuits.
|
||||
var/required_shells = null
|
||||
|
||||
/// Called when the option ports should be set up
|
||||
/obj/item/circuit_component/proc/populate_options()
|
||||
return
|
||||
|
||||
/// Extension of add_input_port. Simplifies the code to make an option port to reduce boilerplate
|
||||
/obj/item/circuit_component/proc/add_option_port(name, list/list_to_use)
|
||||
return add_input_port(name, PORT_TYPE_OPTION, port_type = /datum/port/input/option, extra_args = list("possible_options" = list_to_use))
|
||||
|
||||
/obj/item/circuit_component/Initialize()
|
||||
. = ..()
|
||||
if(name == COMPONENT_DEFAULT_NAME)
|
||||
name = "[lowertext(display_name)] [COMPONENT_DEFAULT_NAME]"
|
||||
populate_options()
|
||||
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/item/circuit_component/LateInitialize()
|
||||
. = ..()
|
||||
if(circuit_flags & CIRCUIT_FLAG_INPUT_SIGNAL)
|
||||
trigger_input = add_input_port("Trigger", PORT_TYPE_SIGNAL)
|
||||
if(circuit_flags & CIRCUIT_FLAG_OUTPUT_SIGNAL)
|
||||
trigger_output = add_output_port("Triggered", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/Destroy()
|
||||
if(parent)
|
||||
// Prevents a Destroy() recursion
|
||||
var/obj/item/integrated_circuit/old_parent = parent
|
||||
parent = null
|
||||
old_parent.remove_component(src)
|
||||
|
||||
trigger_input = null
|
||||
trigger_output = null
|
||||
|
||||
QDEL_LIST(output_ports)
|
||||
QDEL_LIST(input_ports)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Called when a shell is registered from the component/the component is added to a circuit.
|
||||
*
|
||||
* Register all signals here on the shell.
|
||||
* Arguments:
|
||||
* * shell - Shell being registered
|
||||
*/
|
||||
/obj/item/circuit_component/proc/register_shell(atom/movable/shell)
|
||||
return
|
||||
|
||||
/**
|
||||
* Called when a shell is unregistered from the component/the component is removed from a circuit.
|
||||
*
|
||||
* Unregister all signals here on the shell.
|
||||
* Arguments:
|
||||
* * shell - Shell being unregistered
|
||||
*/
|
||||
/obj/item/circuit_component/proc/unregister_shell(atom/movable/shell)
|
||||
return
|
||||
|
||||
/**
|
||||
* Disconnects a component from other components
|
||||
*
|
||||
* Disconnects both the input and output ports of the component
|
||||
*/
|
||||
/obj/item/circuit_component/proc/disconnect()
|
||||
for(var/datum/port/output/port_to_disconnect as anything in output_ports)
|
||||
port_to_disconnect.disconnect_all()
|
||||
|
||||
for(var/datum/port/input/port_to_disconnect as anything in input_ports)
|
||||
port_to_disconnect.disconnect_all()
|
||||
|
||||
/**
|
||||
* Adds an input port and returns it
|
||||
*
|
||||
* Arguments:
|
||||
* * name - The name of the input port
|
||||
* * type - The datatype it handles
|
||||
* * trigger - Whether this input port triggers an update on the component when updated.
|
||||
*/
|
||||
/obj/item/circuit_component/proc/add_input_port(name, type, trigger = TRUE, default = null, index = null, port_type = /datum/port/input, extra_args = null)
|
||||
var/list/arguments = list(src)
|
||||
arguments += args
|
||||
if(extra_args)
|
||||
arguments += extra_args
|
||||
var/datum/port/input/input_port = new port_type(arglist(arguments))
|
||||
if(index)
|
||||
input_ports.Insert(index, input_port)
|
||||
else
|
||||
input_ports += input_port
|
||||
if(parent)
|
||||
SStgui.update_uis(parent)
|
||||
return input_port
|
||||
|
||||
/**
|
||||
* Removes an input port and deletes it. This will not cleanup any references made by derivatives of the circuit component
|
||||
*
|
||||
* Arguments:
|
||||
* * input_port - The input port to remove.
|
||||
*/
|
||||
/obj/item/circuit_component/proc/remove_input_port(datum/port/input/input_port)
|
||||
input_ports -= input_port
|
||||
qdel(input_port)
|
||||
if(parent)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/**
|
||||
* Adds an output port and returns it
|
||||
*
|
||||
* Arguments:
|
||||
* * name - The name of the output port
|
||||
* * type - The datatype it handles.
|
||||
*/
|
||||
/obj/item/circuit_component/proc/add_output_port(name, type)
|
||||
var/list/arguments = list(src)
|
||||
arguments += args
|
||||
var/datum/port/output/output_port = new(arglist(arguments))
|
||||
output_ports += output_port
|
||||
return output_port
|
||||
|
||||
/**
|
||||
* Removes an output port and deletes it. This will not cleanup any references made by derivatives of the circuit component
|
||||
*
|
||||
* Arguments:
|
||||
* * output_port - The output port to remove.
|
||||
*/
|
||||
/obj/item/circuit_component/proc/remove_output_port(datum/port/output/output_port)
|
||||
output_ports -= output_port
|
||||
qdel(output_port)
|
||||
if(parent)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/**
|
||||
* Called whenever an input is received from one of the ports.
|
||||
*
|
||||
* Return value indicates that the circuit should not do anything. Also prevents an output signal.
|
||||
* Arguments:
|
||||
* * port - Can be null. The port that sent the input
|
||||
*/
|
||||
/obj/item/circuit_component/proc/input_received(datum/port/input/port)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(!parent?.on)
|
||||
return TRUE
|
||||
|
||||
if(!parent.admin_only)
|
||||
if(circuit_flags & CIRCUIT_FLAG_ADMIN)
|
||||
message_admins("[display_name] tried to execute on [parent.get_creator_admin()] that has admin_only set to 0")
|
||||
return TRUE
|
||||
|
||||
var/obj/item/stock_parts/cell/cell = parent.get_cell()
|
||||
if(!cell?.use(power_usage_per_input))
|
||||
return TRUE
|
||||
|
||||
if((circuit_flags & CIRCUIT_FLAG_INPUT_SIGNAL) && !COMPONENT_TRIGGERED_BY(trigger_input, port))
|
||||
return TRUE
|
||||
|
||||
/// Called when this component is about to be added to an integrated_circuit.
|
||||
/obj/item/circuit_component/proc/add_to(obj/item/integrated_circuit/added_to)
|
||||
return TRUE
|
||||
|
||||
/// Called when this component is removed from an integrated_circuit.
|
||||
/obj/item/circuit_component/proc/removed_from(obj/item/integrated_circuit/removed_from)
|
||||
return
|
||||
|
||||
/**
|
||||
* Gets the UI notices to be displayed on the CircuitInfo panel.
|
||||
*
|
||||
* Returns a list of buttons in the following format
|
||||
* list(
|
||||
* "icon" = ICON(string)
|
||||
* "content" = CONTENT(string)
|
||||
* "color" = COLOR(string, not a hex)
|
||||
* )
|
||||
*/
|
||||
/obj/item/circuit_component/proc/get_ui_notices()
|
||||
. = list()
|
||||
|
||||
if(!removable)
|
||||
. += create_ui_notice("Unremovable", "red", "lock")
|
||||
|
||||
if(length(required_shells))
|
||||
. += create_ui_notice("Supported Shells:", "green", "notes-medical")
|
||||
for(var/atom/movable/shell as anything in required_shells)
|
||||
. += create_ui_notice(initial(shell.name), "green", "plus-square")
|
||||
|
||||
if(length(input_ports))
|
||||
. += create_ui_notice("Power Usage Per Input: [power_usage_per_input]", "orange", "bolt")
|
||||
|
||||
/**
|
||||
* Creates a UI notice entry to be used in get_ui_notices()
|
||||
*
|
||||
* Returns a list that can then be added to the return list in get_ui_notices()
|
||||
*/
|
||||
/obj/item/circuit_component/proc/create_ui_notice(content, color, icon)
|
||||
SHOULD_BE_PURE(TRUE)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
return list(list(
|
||||
"icon" = icon,
|
||||
"content" = content,
|
||||
"color" = color,
|
||||
))
|
||||
|
||||
/**
|
||||
* Creates a table UI notice entry to be used in get_ui_notices()
|
||||
*
|
||||
* Returns a list that can then be added to the return list in get_ui_notices()
|
||||
* Used by components to list their available columns. Recommended to use at the end of get_ui_notices()
|
||||
*/
|
||||
/obj/item/circuit_component/proc/create_table_notices(list/entries)
|
||||
SHOULD_BE_PURE(TRUE)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
. = list()
|
||||
. += create_ui_notice("Available Columns:", "grey", "question-circle")
|
||||
|
||||
|
||||
for(var/entry in entries)
|
||||
. += create_ui_notice("Column Name: '[entry]'", "grey", "columns")
|
||||
|
||||
/obj/item/circuit_component/proc/register_usb_parent(atom/movable/parent)
|
||||
return
|
||||
|
||||
/obj/item/circuit_component/proc/unregister_usb_parent(atom/movable/parent)
|
||||
return
|
||||
@@ -0,0 +1,394 @@
|
||||
/// Component printer, creates components for integrated circuits.
|
||||
/obj/machinery/component_printer
|
||||
name = "component printer"
|
||||
desc = "Produces components for the creation of integrated circuits."
|
||||
icon = 'icons/obj/wiremod_fab.dmi'
|
||||
icon_state = "fab-idle"
|
||||
circuit = /obj/item/circuitboard/machine/component_printer
|
||||
|
||||
/// The internal material bus
|
||||
var/datum/component/remote_materials/materials
|
||||
|
||||
density = TRUE
|
||||
|
||||
/// The techweb the printer will get researched designs from
|
||||
var/datum/techweb/techweb
|
||||
|
||||
/obj/machinery/component_printer/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
techweb = SSresearch.science_tech
|
||||
|
||||
materials = AddComponent( \
|
||||
/datum/component/remote_materials, \
|
||||
"component_printer", \
|
||||
mapload, \
|
||||
mat_container_flags = BREAKDOWN_FLAGS_LATHE, \
|
||||
)
|
||||
|
||||
/obj/machinery/component_printer/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ComponentPrinter", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/component_printer/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet/sheetmaterials)
|
||||
)
|
||||
|
||||
/obj/machinery/component_printer/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
switch (action)
|
||||
if ("print")
|
||||
var/design_id = params["designId"]
|
||||
if (!techweb.researched_designs[design_id])
|
||||
return TRUE
|
||||
|
||||
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
|
||||
if (!(design.build_type & COMPONENT_PRINTER))
|
||||
return TRUE
|
||||
|
||||
if (materials.on_hold())
|
||||
say("Mineral access is on hold, please contact the quartermaster.")
|
||||
return TRUE
|
||||
|
||||
if (!materials.mat_container?.has_materials(design.materials))
|
||||
say("Not enough materials.")
|
||||
return TRUE
|
||||
|
||||
balloon_alert_to_viewers("printed [design.name]")
|
||||
materials.mat_container?.use_materials(design.materials)
|
||||
materials.silo_log(src, "printed", -1, design.name, design.materials)
|
||||
var/atom/printed_design = new design.build_path(drop_location())
|
||||
printed_design.pixel_x = printed_design.base_pixel_x + rand(-5, 5)
|
||||
printed_design.pixel_y = printed_design.base_pixel_y + rand(-5, 5)
|
||||
if ("remove_mat")
|
||||
var/datum/material/material = locate(params["ref"])
|
||||
var/amount = text2num(params["amount"])
|
||||
|
||||
if (!amount)
|
||||
return TRUE
|
||||
|
||||
// SAFETY: eject_sheets checks for valid mats
|
||||
materials.eject_sheets(material, amount)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/component_printer/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["materials"] = materials.mat_container.ui_data()
|
||||
return data
|
||||
|
||||
/obj/machinery/component_printer/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/designs = list()
|
||||
|
||||
// for (var/datum/design/component/component_design_type as anything in subtypesof(/datum/design/component))
|
||||
for (var/researched_design_id in techweb.researched_designs)
|
||||
var/datum/design/design = SSresearch.techweb_design_by_id(researched_design_id)
|
||||
if (!(design.build_type & COMPONENT_PRINTER))
|
||||
continue
|
||||
|
||||
designs[researched_design_id] = list(
|
||||
"name" = design.name,
|
||||
"description" = design.desc,
|
||||
"materials" = get_material_cost_data(design.materials),
|
||||
"categories" = design.category,
|
||||
)
|
||||
|
||||
data["designs"] = designs
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/component_printer/crowbar_act(mob/living/user, obj/item/tool)
|
||||
if(..())
|
||||
return TRUE
|
||||
return default_deconstruction_crowbar(tool)
|
||||
|
||||
/obj/machinery/component_printer/screwdriver_act(mob/living/user, obj/item/tool)
|
||||
if(..())
|
||||
return TRUE
|
||||
return default_deconstruction_screwdriver(user, "fab-o", "fab-idle", tool)
|
||||
|
||||
/obj/machinery/component_printer/proc/get_material_cost_data(list/materials)
|
||||
var/list/data = list()
|
||||
|
||||
for (var/datum/material/material_type as anything in materials)
|
||||
data[initial(material_type.name)] = materials[material_type]
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/circuitboard/machine/component_printer
|
||||
name = "\improper Component Printer (Machine Board)"
|
||||
greyscale_colors = CIRCUIT_COLOR_SCIENCE
|
||||
build_path = /obj/machinery/component_printer
|
||||
req_components = list(
|
||||
/obj/item/stock_parts/matter_bin = 2,
|
||||
/obj/item/stock_parts/manipulator = 2,
|
||||
/obj/item/reagent_containers/glass/beaker = 2,
|
||||
)
|
||||
|
||||
/obj/machinery/debug_component_printer
|
||||
name = "debug component printer"
|
||||
desc = "Produces components for the creation of integrated circuits."
|
||||
icon = 'icons/obj/wiremod_fab.dmi'
|
||||
icon_state = "fab-idle"
|
||||
|
||||
/// All of the possible circuit designs stored by this debug printer
|
||||
var/list/all_circuit_designs
|
||||
|
||||
density = TRUE
|
||||
|
||||
/obj/machinery/debug_component_printer/Initialize()
|
||||
. = ..()
|
||||
all_circuit_designs = list()
|
||||
|
||||
for(var/id in SSresearch.techweb_designs)
|
||||
var/datum/design/design = SSresearch.techweb_design_by_id(id)
|
||||
if((design.build_type & COMPONENT_PRINTER) && design.build_path)
|
||||
all_circuit_designs[design.build_path] = list(
|
||||
"name" = design.name,
|
||||
"description" = design.desc,
|
||||
"materials" = design.materials,
|
||||
"categories" = design.category
|
||||
)
|
||||
|
||||
for(var/obj/item/circuit_component/component as anything in subtypesof(/obj/item/circuit_component))
|
||||
var/categories = list("Inaccessible")
|
||||
if(initial(component.circuit_flags) & CIRCUIT_FLAG_ADMIN)
|
||||
categories = list("Admin")
|
||||
if(!(component in all_circuit_designs))
|
||||
all_circuit_designs[component] = list(
|
||||
"name" = initial(component.display_name),
|
||||
"description" = initial(component.desc),
|
||||
"materials" = list(),
|
||||
"categories" = categories,
|
||||
)
|
||||
|
||||
/obj/machinery/debug_component_printer/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ComponentPrinter", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/debug_component_printer/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet/sheetmaterials)
|
||||
)
|
||||
|
||||
/obj/machinery/debug_component_printer/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
switch (action)
|
||||
if ("print")
|
||||
var/build_path = text2path(params["designId"])
|
||||
if (!build_path)
|
||||
return TRUE
|
||||
|
||||
var/list/design = all_circuit_designs[build_path]
|
||||
if(!design)
|
||||
return TRUE
|
||||
|
||||
balloon_alert_to_viewers("printed [design["name"]]")
|
||||
var/atom/printed_design = new build_path(drop_location())
|
||||
printed_design.pixel_x = printed_design.base_pixel_x + rand(-5, 5)
|
||||
printed_design.pixel_y = printed_design.base_pixel_y + rand(-5, 5)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/debug_component_printer/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["materials"] = list()
|
||||
data["designs"] = all_circuit_designs
|
||||
|
||||
return data
|
||||
|
||||
/// Module duplicator, allows you to save and recreate module components.
|
||||
/obj/machinery/module_duplicator
|
||||
name = "module duplicator"
|
||||
desc = "Allows you to duplicate module components so that you don't have to recreate them. Scan a module component over this machine to add it as an entry."
|
||||
icon = 'icons/obj/wiremod_fab.dmi'
|
||||
icon_state = "module-fab-idle"
|
||||
circuit = /obj/item/circuitboard/machine/module_duplicator
|
||||
|
||||
/// The internal material bus
|
||||
var/datum/component/remote_materials/materials
|
||||
|
||||
density = TRUE
|
||||
|
||||
var/list/scanned_designs = list()
|
||||
|
||||
var/cost_per_component = 1000
|
||||
|
||||
/obj/machinery/module_duplicator/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
materials = AddComponent( \
|
||||
/datum/component/remote_materials, \
|
||||
"module_duplicator", \
|
||||
mapload, \
|
||||
mat_container_flags = BREAKDOWN_FLAGS_LATHE, \
|
||||
)
|
||||
|
||||
/obj/machinery/module_duplicator/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ComponentPrinter", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/module_duplicator/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet/sheetmaterials)
|
||||
)
|
||||
|
||||
/obj/machinery/module_duplicator/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
switch (action)
|
||||
if ("print")
|
||||
var/design_id = text2num(params["designId"])
|
||||
|
||||
if (design_id < 1 || design_id > length(scanned_designs))
|
||||
return TRUE
|
||||
|
||||
var/list/design = scanned_designs[design_id]
|
||||
|
||||
if (materials.on_hold())
|
||||
say("Mineral access is on hold, please contact the quartermaster.")
|
||||
return TRUE
|
||||
|
||||
if (!materials.mat_container?.has_materials(design["materials"]))
|
||||
say("Not enough materials.")
|
||||
return TRUE
|
||||
|
||||
balloon_alert_to_viewers("printed [design["name"]]")
|
||||
materials.mat_container?.use_materials(design["materials"])
|
||||
materials.silo_log(src, "printed", -1, design["name"], design["materials"])
|
||||
print_module(design)
|
||||
if ("remove_mat")
|
||||
var/datum/material/material = locate(params["ref"])
|
||||
var/amount = text2num(params["amount"])
|
||||
|
||||
if (!amount)
|
||||
return TRUE
|
||||
|
||||
// SAFETY: eject_sheets checks for valid mats
|
||||
materials.eject_sheets(material, amount)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/module_duplicator/proc/print_module(list/design)
|
||||
flick("module-fab-print", src)
|
||||
addtimer(CALLBACK(src, .proc/finish_module_print, design), 1.6 SECONDS)
|
||||
|
||||
/obj/machinery/module_duplicator/proc/finish_module_print(list/design)
|
||||
var/obj/item/circuit_component/module/module = new(drop_location())
|
||||
module.load_data_from_list(design["dupe_data"])
|
||||
module.pixel_x = module.base_pixel_x + rand(-5, 5)
|
||||
module.pixel_y = module.base_pixel_y + rand(-5, 5)
|
||||
|
||||
/obj/machinery/module_duplicator/attackby(obj/item/weapon, mob/user, params)
|
||||
if(!istype(weapon, /obj/item/circuit_component/module))
|
||||
return ..()
|
||||
|
||||
var/obj/item/circuit_component/module/module = weapon
|
||||
if(module.circuit_flags & CIRCUIT_FLAG_UNDUPEABLE)
|
||||
balloon_alert(user, "module cannot be saved!")
|
||||
return ..()
|
||||
|
||||
if(module.display_name == initial(module.display_name))
|
||||
balloon_alert(user, "module needs a name!")
|
||||
return ..()
|
||||
|
||||
for(var/list/component_data as anything in scanned_designs)
|
||||
if(component_data["name"] == module.display_name)
|
||||
balloon_alert(user, "module name already exists!")
|
||||
return ..()
|
||||
|
||||
var/total_cost = 0
|
||||
for(var/obj/item/circuit_component/component as anything in module.internal_circuit.attached_components)
|
||||
if(component.circuit_flags & CIRCUIT_FLAG_UNDUPEABLE)
|
||||
balloon_alert(user, "module contains prohibited components!")
|
||||
return ..()
|
||||
|
||||
total_cost += cost_per_component
|
||||
|
||||
var/list/data = list()
|
||||
|
||||
data["dupe_data"] = list()
|
||||
module.save_data_to_list(data["dupe_data"])
|
||||
|
||||
data["name"] = module.display_name
|
||||
data["desc"] = "A module that has been loaded in by [user]."
|
||||
data["materials"] = list(/datum/material/glass = total_cost)
|
||||
|
||||
flick("module-fab-scan", src)
|
||||
addtimer(CALLBACK(src, .proc/finish_module_scan, user, data), 1.4 SECONDS)
|
||||
|
||||
/obj/machinery/module_duplicator/proc/finish_module_scan(mob/user, data)
|
||||
scanned_designs += list(data)
|
||||
|
||||
balloon_alert(user, "module has been saved.")
|
||||
playsound(src, 'sound/machines/ping.ogg', 50)
|
||||
|
||||
/obj/machinery/module_duplicator/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["materials"] = materials.mat_container.ui_data()
|
||||
return data
|
||||
|
||||
/obj/machinery/module_duplicator/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/designs = list()
|
||||
|
||||
var/index = 1
|
||||
for (var/list/design as anything in scanned_designs)
|
||||
designs["[index]"] = list(
|
||||
"name" = design["name"],
|
||||
"description" = design["desc"],
|
||||
"materials" = get_material_cost_data(design["materials"]),
|
||||
"categories" = list("Circuitry"),
|
||||
)
|
||||
index++
|
||||
|
||||
data["designs"] = designs
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/module_duplicator/crowbar_act(mob/living/user, obj/item/tool)
|
||||
if(..())
|
||||
return TRUE
|
||||
return default_deconstruction_crowbar(tool)
|
||||
|
||||
/obj/machinery/module_duplicator/screwdriver_act(mob/living/user, obj/item/tool)
|
||||
if(..())
|
||||
return TRUE
|
||||
return default_deconstruction_screwdriver(user, "module-fab-o", "module-fab-idle", tool)
|
||||
|
||||
/obj/machinery/module_duplicator/proc/get_material_cost_data(list/materials)
|
||||
var/list/data = list()
|
||||
|
||||
for (var/datum/material/material_type as anything in materials)
|
||||
data[initial(material_type.name)] = materials[material_type]
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/circuitboard/machine/module_duplicator
|
||||
name = "\improper Module Duplicator (Machine Board)"
|
||||
greyscale_colors = CIRCUIT_COLOR_SCIENCE
|
||||
build_path = /obj/machinery/module_duplicator
|
||||
req_components = list(
|
||||
/obj/item/stock_parts/matter_bin = 2,
|
||||
/obj/item/stock_parts/manipulator = 2,
|
||||
/obj/item/reagent_containers/glass/beaker = 2,
|
||||
)
|
||||
@@ -0,0 +1,91 @@
|
||||
// An assoc list of all the possible datatypes.
|
||||
GLOBAL_LIST_INIT(circuit_datatypes, generate_circuit_datatypes())
|
||||
|
||||
/proc/generate_circuit_datatypes()
|
||||
var/list/datatypes_by_key = list()
|
||||
for(var/datum/circuit_datatype/type as anything in subtypesof(/datum/circuit_datatype))
|
||||
if(!initial(type.datatype))
|
||||
continue
|
||||
datatypes_by_key[initial(type.datatype)] = new type()
|
||||
return datatypes_by_key
|
||||
|
||||
/**
|
||||
* A circuit datatype. Used to determine the datatype of a port and also handle any additional behaviour.
|
||||
*/
|
||||
/datum/circuit_datatype
|
||||
/// The key. Used to identify the datatype. Should be a define.
|
||||
var/datatype
|
||||
|
||||
/// The color of the port in the UI. Doesn't work with hex colours.
|
||||
var/color = "blue"
|
||||
|
||||
/// The flags of the circuit datatype
|
||||
var/datatype_flags = 0
|
||||
|
||||
/**
|
||||
* Returns the value to be set for the port
|
||||
*
|
||||
* Used for implicit conversions between outputs and inputs (e.g. number -> string)
|
||||
* and applying/removing signals on inputs
|
||||
*/
|
||||
/datum/circuit_datatype/proc/convert_value(datum/port/port, value_to_convert)
|
||||
return value_to_convert
|
||||
|
||||
/**
|
||||
* Determines if a datatype is compatible with another port of a different type.
|
||||
* Note: This is ALWAYS called on the input port, never on the output port.
|
||||
* Inputs need to care about what types they're receiving, output ports don't have to care.
|
||||
*
|
||||
* Arguments:
|
||||
* * datatype_to_check - The datatype to check
|
||||
*/
|
||||
/datum/circuit_datatype/proc/can_receive_from_datatype(datatype_to_check)
|
||||
return datatype == datatype_to_check // This is already done by default on the input port.
|
||||
|
||||
/**
|
||||
* Called when the datatype is given to a port.
|
||||
*
|
||||
* Arguments:
|
||||
* * gained_port - The gained port.
|
||||
*/
|
||||
/datum/circuit_datatype/proc/on_gain(datum/port/gained_port)
|
||||
return
|
||||
|
||||
/**
|
||||
* Called when the datatype is removed from a port.
|
||||
*
|
||||
* Arguments:
|
||||
* * lost_port - The removed port.
|
||||
*/
|
||||
/datum/circuit_datatype/proc/on_loss(datum/port/lost_port)
|
||||
return
|
||||
|
||||
/**
|
||||
* Determines if a port is compatible with this datatype.
|
||||
* This WILL throw a runtime if it returns false. This is for sanity checking and it should not return false
|
||||
* unless under extraordinary circumstances or people fail to write proper code.
|
||||
*
|
||||
* Arguments:
|
||||
* * port - The port to check if it is compatible.
|
||||
*/
|
||||
/datum/circuit_datatype/proc/is_compatible(datum/port/port)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* The data to send to the UI attached to the port. Received by the type in FUNDAMENTAL_PORT_TYPES
|
||||
*
|
||||
* Arguments:
|
||||
* * port - The port sending the data.
|
||||
*/
|
||||
/datum/circuit_datatype/proc/datatype_ui_data(datum/port/port)
|
||||
return
|
||||
|
||||
/**
|
||||
* When an input is manually set by a player. This is where extra sanitizing can happen. Will still call convert_value()
|
||||
*
|
||||
* Arguments:
|
||||
* * port - The port sending the data.
|
||||
* *
|
||||
*/
|
||||
/datum/circuit_datatype/proc/handle_manual_input(datum/port/input/port, mob/user, user_input)
|
||||
return user_input
|
||||
@@ -0,0 +1,227 @@
|
||||
#define LOG_ERROR(list, error) if(list) { list.Add(error) }
|
||||
|
||||
// Determines if a port can have a predefined input value if it is of this type.
|
||||
GLOBAL_LIST_INIT(circuit_dupe_whitelisted_types, list(
|
||||
PORT_TYPE_NUMBER,
|
||||
PORT_TYPE_STRING,
|
||||
PORT_TYPE_LIST,
|
||||
PORT_TYPE_ANY,
|
||||
PORT_TYPE_OPTION,
|
||||
))
|
||||
|
||||
/// Loads a circuit based on json data at a location. Can also load usb connections, such as arrest consoles.
|
||||
/obj/item/integrated_circuit/proc/load_circuit_data(json_data, list/errors)
|
||||
var/list/general_data = json_decode(json_data)
|
||||
|
||||
if(!general_data)
|
||||
LOG_ERROR(errors, "Invalid json format!")
|
||||
return
|
||||
|
||||
if(general_data["display_name"])
|
||||
set_display_name(general_data["display_name"])
|
||||
|
||||
var/list/variable_data = general_data["variables"]
|
||||
for(var/list/variable as anything in variable_data)
|
||||
var/variable_name = variable["name"]
|
||||
circuit_variables[variable_name] = new /datum/circuit_variable(variable_name, variable["datatype"])
|
||||
|
||||
admin_only = general_data["admin_only"]
|
||||
|
||||
var/list/circuit_data = general_data["components"]
|
||||
var/list/identifiers_to_circuit = list()
|
||||
for(var/identifier in circuit_data)
|
||||
var/list/component_data = circuit_data[identifier]
|
||||
var/type = text2path(component_data["type"])
|
||||
if(!ispath(type, /obj/item/circuit_component))
|
||||
LOG_ERROR(errors, "Invalid path for circuit component, expected [/obj/item/circuit_component], got [type]")
|
||||
continue
|
||||
var/obj/item/circuit_component/component = load_component(type)
|
||||
identifiers_to_circuit[identifier] = component
|
||||
component.load_data_from_list(component_data)
|
||||
|
||||
var/list/input_ports_data = component_data["input_ports_stored_data"]
|
||||
for(var/port_name in input_ports_data)
|
||||
var/datum/port/input/port
|
||||
var/list/port_data = input_ports_data[port_name]
|
||||
for(var/datum/port/input/port_to_check as anything in component.input_ports)
|
||||
if(port_to_check.name == port_name)
|
||||
port = port_to_check
|
||||
break
|
||||
|
||||
port.set_input(port_data["stored_data"])
|
||||
|
||||
var/list/external_objects = general_data["external_objects"]
|
||||
for(var/identifier in external_objects)
|
||||
var/list/object_data = external_objects[identifier]
|
||||
var/type = text2path(object_data["type"])
|
||||
if(!ispath(type))
|
||||
LOG_ERROR(errors, "Invalid path for external object, expected a path, got [type]")
|
||||
continue
|
||||
var/atom/movable/object = new type(drop_location())
|
||||
var/list/connected_components = list()
|
||||
for(var/component_id in object_data["connected_components"])
|
||||
var/obj/item/circuit_component/component = identifiers_to_circuit[component_id]
|
||||
if(!component)
|
||||
continue
|
||||
connected_components += component
|
||||
SEND_SIGNAL(object, COMSIG_MOVABLE_CIRCUIT_LOADED, src, connected_components)
|
||||
|
||||
for(var/identifier in identifiers_to_circuit)
|
||||
var/obj/item/circuit_component/component = identifiers_to_circuit[identifier]
|
||||
var/list/component_data = circuit_data[identifier]
|
||||
|
||||
var/list/connections = component_data["connections"]
|
||||
for(var/port_name in connections)
|
||||
var/datum/port/input/port
|
||||
var/list/connection_data = connections[port_name]
|
||||
for(var/datum/port/input/port_to_check as anything in component.input_ports)
|
||||
if(port_to_check.name == port_name)
|
||||
port = port_to_check
|
||||
break
|
||||
|
||||
if(!port)
|
||||
LOG_ERROR(errors, "Port [port_name] not found for [component.type].")
|
||||
continue
|
||||
|
||||
if(connection_data["stored_data"])
|
||||
if(!(port.datatype in GLOB.circuit_dupe_whitelisted_types))
|
||||
continue
|
||||
port.set_input(connection_data["stored_data"])
|
||||
continue
|
||||
|
||||
// The || list(connected_data) is for backwards compatibility with when inputs could only be connected to up to one output.
|
||||
for(var/list/output_data in (connection_data["connected_ports"] || list(connection_data)))
|
||||
var/obj/item/circuit_component/connected_component = identifiers_to_circuit[output_data["component_id"]]
|
||||
if(!connected_component)
|
||||
LOG_ERROR(errors, "No connected component found for [component.type] for port [connection_data["port_name"]]. (connected component identifier: [connection_data["component_id"]])")
|
||||
continue
|
||||
|
||||
var/datum/port/output/output_port
|
||||
var/output_port_name = output_data["port_name"]
|
||||
for(var/datum/port/output/port_to_check as anything in connected_component.output_ports)
|
||||
if(port_to_check.name == output_port_name)
|
||||
output_port = port_to_check
|
||||
break
|
||||
|
||||
if(!output_port)
|
||||
LOG_ERROR(errors, "No output port found for [component.type] for port [output_port_name] on component [connected_component.type]")
|
||||
continue
|
||||
|
||||
port.connect(output_port)
|
||||
|
||||
#undef LOG_ERROR
|
||||
|
||||
/// Converts a circuit into json.
|
||||
/obj/item/integrated_circuit/proc/convert_to_json()
|
||||
var/list/circuit_to_identifiers = list()
|
||||
var/list/identifiers = list()
|
||||
var/list/external_objects = list() // Objects that are connected to a component. These objects will be linked to the components.
|
||||
for(var/obj/item/circuit_component/component as anything in attached_components)
|
||||
var/identifier = "[component.type][length(identifiers)]"
|
||||
identifiers += identifier
|
||||
circuit_to_identifiers[component] = identifier
|
||||
var/list/objects = list()
|
||||
SEND_SIGNAL(component, COMSIG_CIRCUIT_COMPONENT_SAVE, objects)
|
||||
|
||||
for(var/atom/movable/object as anything in objects)
|
||||
if(object in external_objects)
|
||||
external_objects[object] += identifier
|
||||
continue
|
||||
external_objects[object] = list(identifier)
|
||||
|
||||
var/list/circuit_data = list()
|
||||
for(var/obj/item/circuit_component/component as anything in circuit_to_identifiers)
|
||||
var/identifier = circuit_to_identifiers[component]
|
||||
var/list/component_data = list()
|
||||
|
||||
component_data["type"] = component.type
|
||||
|
||||
var/list/connections = list()
|
||||
var/list/input_ports_stored_data = list()
|
||||
for(var/datum/port/input/input as anything in component.input_ports)
|
||||
var/list/connection_data = list()
|
||||
if(!length(input.connected_ports))
|
||||
if(isnull(input.value) || !(input.datatype in GLOB.circuit_dupe_whitelisted_types))
|
||||
continue
|
||||
connection_data["stored_data"] = input.value
|
||||
input_ports_stored_data[input.name] = connection_data
|
||||
continue
|
||||
connection_data["connected_ports"] = list()
|
||||
for(var/datum/port/output/output as anything in input.connected_ports)
|
||||
connection_data["connected_ports"] += list(list(
|
||||
"component_id" = circuit_to_identifiers[output.connected_component],
|
||||
"port_name" = output.name,
|
||||
))
|
||||
connections[input.name] = connection_data
|
||||
component_data["connections"] = connections
|
||||
component_data["input_ports_stored_data"] = input_ports_stored_data
|
||||
|
||||
component.save_data_to_list(component_data)
|
||||
circuit_data[identifier] = component_data
|
||||
|
||||
var/external_objects_key = list()
|
||||
for(var/atom/movable/object as anything in external_objects)
|
||||
var/list/new_data = list()
|
||||
new_data["type"] = object.type
|
||||
new_data["connected_components"] = external_objects[object]
|
||||
external_objects_key["[object.type][length(external_objects_key)]"] = new_data
|
||||
|
||||
var/list/general_data = list()
|
||||
general_data["components"] = circuit_data
|
||||
general_data["external_objects"] = external_objects_key
|
||||
general_data["display_name"] = display_name
|
||||
general_data["admin_only"] = admin_only
|
||||
|
||||
var/list/variables = list()
|
||||
for(var/variable_identifier in circuit_variables)
|
||||
var/list/new_data = list()
|
||||
var/datum/circuit_variable/variable = circuit_variables[variable_identifier]
|
||||
new_data["name"] = variable.name
|
||||
new_data["datatype"] = variable.datatype
|
||||
variables += list(new_data)
|
||||
general_data["variables"] = variables
|
||||
|
||||
return json_encode(general_data)
|
||||
|
||||
/obj/item/integrated_circuit/proc/load_component(type)
|
||||
var/obj/item/circuit_component/component = new type(src)
|
||||
add_component(component)
|
||||
return component
|
||||
|
||||
/// Saves data to a list. Shouldn't be used unless you are quite literally saving the data of a component to a list. Input value is the list to save the data to
|
||||
/obj/item/circuit_component/proc/save_data_to_list(list/component_data)
|
||||
component_data["rel_x"] = rel_x
|
||||
component_data["rel_y"] = rel_y
|
||||
|
||||
/// Loads data from a list
|
||||
/obj/item/circuit_component/proc/load_data_from_list(list/component_data)
|
||||
rel_x = component_data["rel_x"]
|
||||
rel_y = component_data["rel_y"]
|
||||
|
||||
/client/proc/load_circuit()
|
||||
set name = "Load Circuit"
|
||||
set category = "Admin.Fun"
|
||||
|
||||
if(!check_rights(R_VAREDIT))
|
||||
return
|
||||
|
||||
var/list/errors = list()
|
||||
|
||||
var/option = alert(usr, "Load by file or direct input?", "Load by file or string", "File", "Direct Input")
|
||||
var/txt
|
||||
switch(option)
|
||||
if("File")
|
||||
txt = file2text(tgui_input_num(usr, "Input File") as file|null)
|
||||
if("Direct Input")
|
||||
txt = tgui_input_num(usr, "Input JSON", "Input JSON") as text|null
|
||||
|
||||
if(!txt)
|
||||
return
|
||||
|
||||
var/obj/item/integrated_circuit/loaded/circuit = new(mob.drop_location())
|
||||
circuit.load_circuit_data(txt, errors)
|
||||
|
||||
if(length(errors))
|
||||
to_chat(src, span_warning("The following errors were found whilst compiling the circuit data:"))
|
||||
for(var/error in errors)
|
||||
to_chat(src, span_warning(error))
|
||||
@@ -0,0 +1,600 @@
|
||||
/// A list of all integrated circuits
|
||||
GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit)
|
||||
|
||||
/**
|
||||
* # Integrated Circuitboard
|
||||
*
|
||||
* A circuitboard that holds components that work together
|
||||
*
|
||||
* Has a limited amount of power.
|
||||
*/
|
||||
/obj/item/integrated_circuit
|
||||
name = "integrated circuit"
|
||||
desc = "By inserting components and a cell into this, wiring them up, and putting them into a shell, anyone can pretend to be a programmer."
|
||||
icon = 'icons/obj/module.dmi'
|
||||
icon_state = "integrated_circuit"
|
||||
inhand_icon_state = "electronic"
|
||||
|
||||
/// The name that appears on the shell.
|
||||
var/display_name = ""
|
||||
|
||||
/// The max length of the name.
|
||||
var/label_max_length = 24
|
||||
|
||||
/// The power of the integrated circuit
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
|
||||
/// The shell that this circuitboard is attached to. Used by components.
|
||||
var/atom/movable/shell
|
||||
|
||||
/// The attached components
|
||||
var/list/obj/item/circuit_component/attached_components = list()
|
||||
|
||||
/// Whether the integrated circuit is on or not. Handled by the shell.
|
||||
var/on = FALSE
|
||||
|
||||
/// Whether the integrated circuit is locked or not. Handled by the shell.
|
||||
var/locked = FALSE
|
||||
|
||||
/// Whether the integrated circuit is admin only. Disables power usage and allows admin circuits to be attached, at the cost of making it inaccessible to regular users.
|
||||
var/admin_only = FALSE
|
||||
|
||||
/// The ID that is authorized to unlock/lock the shell so that the circuit can/cannot be removed.
|
||||
var/datum/weakref/owner_id
|
||||
|
||||
/// The current examined component. Used in IntegratedCircuit UI
|
||||
var/datum/weakref/examined_component
|
||||
|
||||
/// Set by the shell. Holds the reference to the owner who inserted the component into the shell.
|
||||
var/datum/weakref/inserter_mind
|
||||
|
||||
/// Variables stored on this integrated circuit. with a `variable_name = value` structure
|
||||
var/list/datum/circuit_variable/circuit_variables = list()
|
||||
|
||||
/// The maximum amount of setters and getters a circuit can have
|
||||
var/max_setters_and_getters = 30
|
||||
|
||||
/// The current setter and getter count the circuit has.
|
||||
var/setter_and_getter_count = 0
|
||||
|
||||
/// X position of the examined_component
|
||||
var/examined_rel_x = 0
|
||||
|
||||
/// Y position of the examined component
|
||||
var/examined_rel_y = 0
|
||||
|
||||
/// The X position of the screen. Used for adding components
|
||||
var/screen_x = 0
|
||||
|
||||
/// The Y position of the screen. Used for adding components.
|
||||
var/screen_y = 0
|
||||
|
||||
/obj/item/integrated_circuit/Initialize()
|
||||
. = ..()
|
||||
|
||||
GLOB.integrated_circuits += src
|
||||
|
||||
RegisterSignal(src, COMSIG_ATOM_USB_CABLE_TRY_ATTACH, .proc/on_atom_usb_cable_try_attach)
|
||||
|
||||
/obj/item/integrated_circuit/loaded/Initialize()
|
||||
. = ..()
|
||||
set_cell(new /obj/item/stock_parts/cell/high(src))
|
||||
|
||||
/obj/item/integrated_circuit/Destroy()
|
||||
for(var/obj/item/circuit_component/to_delete in attached_components)
|
||||
remove_component(to_delete)
|
||||
qdel(to_delete)
|
||||
QDEL_LIST(circuit_variables)
|
||||
attached_components.Cut()
|
||||
shell = null
|
||||
examined_component = null
|
||||
owner_id = null
|
||||
QDEL_NULL(cell)
|
||||
GLOB.integrated_circuits -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/integrated_circuit/examine(mob/user)
|
||||
. = ..()
|
||||
if(cell)
|
||||
. += span_notice("The charge meter reads [cell ? round(cell.percent(), 1) : 0]%.")
|
||||
else
|
||||
. += span_notice("There is no power cell installed.")
|
||||
|
||||
/obj/item/integrated_circuit/proc/set_cell(obj/item/stock_parts/cell_to_set)
|
||||
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_CELL, cell_to_set)
|
||||
cell = cell_to_set
|
||||
|
||||
/obj/item/integrated_circuit/attackby(obj/item/I, mob/living/user, params)
|
||||
. = ..()
|
||||
if(istype(I, /obj/item/circuit_component))
|
||||
add_component_manually(I, user)
|
||||
return
|
||||
|
||||
if(istype(I, /obj/item/stock_parts/cell))
|
||||
if(cell)
|
||||
balloon_alert(user, "there already is a cell inside!")
|
||||
return
|
||||
if(!user.transferItemToLoc(I, src))
|
||||
return
|
||||
set_cell(I)
|
||||
I.add_fingerprint(user)
|
||||
user.visible_message(span_notice("[user] inserts a power cell into [src]."), span_notice("You insert the power cell into [src]."))
|
||||
return
|
||||
|
||||
if(istype(I, /obj/item/card/id))
|
||||
balloon_alert(user, "owner id set for [I]")
|
||||
owner_id = WEAKREF(I)
|
||||
return
|
||||
|
||||
if(I.tool_behaviour == TOOL_SCREWDRIVER)
|
||||
if(!cell)
|
||||
return
|
||||
I.play_tool_sound(src)
|
||||
user.visible_message(span_notice("[user] unscrews the power cell from [src]."), span_notice("You unscrew the power cell from [src]."))
|
||||
cell.forceMove(drop_location())
|
||||
set_cell(null)
|
||||
return
|
||||
|
||||
/**
|
||||
* Registers an movable atom as a shell
|
||||
*
|
||||
* No functionality is done here. This is so that input components
|
||||
* can properly register any signals on the shell.
|
||||
* Arguments:
|
||||
* * new_shell - The new shell to register.
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/set_shell(atom/movable/new_shell)
|
||||
remove_current_shell()
|
||||
set_on(TRUE)
|
||||
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_SHELL, new_shell)
|
||||
shell = new_shell
|
||||
RegisterSignal(shell, COMSIG_PARENT_QDELETING, .proc/remove_current_shell)
|
||||
for(var/obj/item/circuit_component/attached_component as anything in attached_components)
|
||||
attached_component.register_shell(shell)
|
||||
// Their input ports may be updated with user values, but the outputs haven't updated
|
||||
// because on is FALSE
|
||||
TRIGGER_CIRCUIT_COMPONENT(attached_component, null)
|
||||
|
||||
/**
|
||||
* Unregisters the current shell attached to this circuit.
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/remove_current_shell()
|
||||
SIGNAL_HANDLER
|
||||
if(!shell)
|
||||
return
|
||||
shell.name = initial(shell.name)
|
||||
for(var/obj/item/circuit_component/attached_component as anything in attached_components)
|
||||
attached_component.unregister_shell(shell)
|
||||
UnregisterSignal(shell, COMSIG_PARENT_QDELETING)
|
||||
shell = null
|
||||
set_on(FALSE)
|
||||
SEND_SIGNAL(src, COMSIG_CIRCUIT_SHELL_REMOVED)
|
||||
|
||||
/obj/item/integrated_circuit/proc/set_on(new_value)
|
||||
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_ON, new_value)
|
||||
on = new_value
|
||||
|
||||
/**
|
||||
* Adds a component to the circuitboard
|
||||
*
|
||||
* Once the component is added, the ports can be attached to other components
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/add_component(obj/item/circuit_component/to_add, mob/living/user)
|
||||
if(to_add.parent)
|
||||
return
|
||||
|
||||
if(SEND_SIGNAL(src, COMSIG_CIRCUIT_ADD_COMPONENT, to_add, user) & COMPONENT_CANCEL_ADD_COMPONENT)
|
||||
return
|
||||
|
||||
if(!to_add.add_to(src))
|
||||
return
|
||||
|
||||
var/success = FALSE
|
||||
if(user)
|
||||
success = user.transferItemToLoc(to_add, src)
|
||||
else
|
||||
success = to_add.forceMove(src)
|
||||
|
||||
if(!success)
|
||||
return
|
||||
|
||||
to_add.rel_x = rand(COMPONENT_MIN_RANDOM_POS, COMPONENT_MAX_RANDOM_POS) - screen_x
|
||||
to_add.rel_y = rand(COMPONENT_MIN_RANDOM_POS, COMPONENT_MAX_RANDOM_POS) - screen_y
|
||||
to_add.parent = src
|
||||
attached_components += to_add
|
||||
RegisterSignal(to_add, COMSIG_MOVABLE_MOVED, .proc/component_move_handler)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
if(shell)
|
||||
to_add.register_shell(shell)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Adds a component to the circuitboard through a manual action.
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/add_component_manually(obj/item/circuit_component/to_add, mob/living/user)
|
||||
if (SEND_SIGNAL(src, COMSIG_CIRCUIT_ADD_COMPONENT_MANUALLY, to_add, user) & COMPONENT_CANCEL_ADD_COMPONENT)
|
||||
return
|
||||
|
||||
return add_component(to_add, user)
|
||||
|
||||
/obj/item/integrated_circuit/proc/component_move_handler(obj/item/circuit_component/source)
|
||||
SIGNAL_HANDLER
|
||||
if(source.loc != src)
|
||||
remove_component(source)
|
||||
|
||||
/**
|
||||
* Removes a component to the circuitboard
|
||||
*
|
||||
* This removes all connects between the ports
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/remove_component(obj/item/circuit_component/to_remove)
|
||||
if(shell)
|
||||
to_remove.unregister_shell(shell)
|
||||
|
||||
UnregisterSignal(to_remove, COMSIG_MOVABLE_MOVED)
|
||||
attached_components -= to_remove
|
||||
to_remove.disconnect()
|
||||
to_remove.parent = null
|
||||
SEND_SIGNAL(to_remove, COMSIG_CIRCUIT_COMPONENT_REMOVED, src)
|
||||
SStgui.update_uis(src)
|
||||
to_remove.removed_from(src)
|
||||
|
||||
/obj/item/integrated_circuit/get_cell()
|
||||
return cell
|
||||
|
||||
/obj/item/integrated_circuit/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/simple/circuit_assets)
|
||||
)
|
||||
|
||||
/obj/item/integrated_circuit/ui_static_data(mob/user)
|
||||
. = list()
|
||||
.["global_basic_types"] = GLOB.wiremod_basic_types
|
||||
.["screen_x"] = screen_x
|
||||
.["screen_y"] = screen_y
|
||||
|
||||
/obj/item/integrated_circuit/ui_data(mob/user)
|
||||
. = list()
|
||||
.["components"] = list()
|
||||
for(var/obj/item/circuit_component/component as anything in attached_components)
|
||||
if (component.circuit_flags & CIRCUIT_FLAG_HIDDEN)
|
||||
.["components"] += null
|
||||
continue
|
||||
|
||||
var/list/component_data = list()
|
||||
component_data["input_ports"] = list()
|
||||
for(var/datum/port/input/port as anything in component.input_ports)
|
||||
var/current_data = port.value
|
||||
if(isatom(current_data)) // Prevent passing the name of the atom.
|
||||
current_data = null
|
||||
var/list/connected_to = list()
|
||||
for(var/datum/port/output/output as anything in port.connected_ports)
|
||||
connected_to += REF(output)
|
||||
component_data["input_ports"] += list(list(
|
||||
"name" = port.name,
|
||||
"type" = port.datatype,
|
||||
"ref" = REF(port), // The ref is the identifier to work out what it is connected to
|
||||
"connected_to" = connected_to,
|
||||
"color" = port.color,
|
||||
"current_data" = current_data,
|
||||
"datatype_data" = port.datatype_ui_data(user),
|
||||
))
|
||||
component_data["output_ports"] = list()
|
||||
for(var/datum/port/output/port as anything in component.output_ports)
|
||||
component_data["output_ports"] += list(list(
|
||||
"name" = port.name,
|
||||
"type" = port.datatype,
|
||||
"ref" = REF(port),
|
||||
"color" = port.color,
|
||||
))
|
||||
|
||||
component_data["name"] = component.display_name
|
||||
component_data["x"] = component.rel_x
|
||||
component_data["y"] = component.rel_y
|
||||
component_data["removable"] = component.removable
|
||||
.["components"] += list(component_data)
|
||||
|
||||
.["variables"] = list()
|
||||
for(var/variable_name in circuit_variables)
|
||||
var/datum/circuit_variable/variable = circuit_variables[variable_name]
|
||||
var/list/variable_data = list()
|
||||
variable_data["name"] = variable.name
|
||||
variable_data["datatype"] = variable.datatype
|
||||
variable_data["color"] = variable.color
|
||||
.["variables"] += list(variable_data)
|
||||
|
||||
|
||||
.["display_name"] = display_name
|
||||
|
||||
var/obj/item/circuit_component/examined
|
||||
if(examined_component)
|
||||
examined = examined_component.resolve()
|
||||
|
||||
.["examined_name"] = examined?.display_name
|
||||
.["examined_desc"] = examined?.desc
|
||||
.["examined_notices"] = examined?.get_ui_notices()
|
||||
.["examined_rel_x"] = examined_rel_x
|
||||
.["examined_rel_y"] = examined_rel_y
|
||||
|
||||
.["is_admin"] = check_rights_for(user.client, R_VAREDIT)
|
||||
|
||||
/obj/item/integrated_circuit/ui_host(mob/user)
|
||||
if(shell)
|
||||
return shell
|
||||
return ..()
|
||||
|
||||
/obj/item/integrated_circuit/can_interact(mob/user)
|
||||
if(locked)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/integrated_circuit/ui_status(mob/user)
|
||||
. = ..()
|
||||
|
||||
if (isobserver(user))
|
||||
. = max(., UI_UPDATE)
|
||||
|
||||
// Extra protection because ui_state will not close the UI if they already have the ui open,
|
||||
// as ui_state is only set during
|
||||
if(admin_only)
|
||||
if(!check_rights_for(user.client, R_VAREDIT))
|
||||
return UI_CLOSE
|
||||
else
|
||||
return UI_INTERACTIVE
|
||||
|
||||
/obj/item/integrated_circuit/ui_state(mob/user)
|
||||
if(!shell)
|
||||
return GLOB.hands_state
|
||||
return GLOB.physical_obscured_state
|
||||
|
||||
/obj/item/integrated_circuit/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "IntegratedCircuit", name)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
#define WITHIN_RANGE(id, table) (id >= 1 && id <= length(table))
|
||||
|
||||
/obj/item/integrated_circuit/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("add_connection")
|
||||
var/input_component_id = text2num(params["input_component_id"])
|
||||
var/output_component_id = text2num(params["output_component_id"])
|
||||
var/input_port_id = text2num(params["input_port_id"])
|
||||
var/output_port_id = text2num(params["output_port_id"])
|
||||
if(!WITHIN_RANGE(input_component_id, attached_components) || !WITHIN_RANGE(output_component_id, attached_components))
|
||||
return
|
||||
var/obj/item/circuit_component/input_component = attached_components[input_component_id]
|
||||
var/obj/item/circuit_component/output_component = attached_components[output_component_id]
|
||||
|
||||
if(!WITHIN_RANGE(input_port_id, input_component.input_ports) || !WITHIN_RANGE(output_port_id, output_component.output_ports))
|
||||
return
|
||||
var/datum/port/input/input_port = input_component.input_ports[input_port_id]
|
||||
var/datum/port/output/output_port = output_component.output_ports[output_port_id]
|
||||
|
||||
if(!input_port.can_receive_from_datatype(output_port.datatype))
|
||||
return
|
||||
input_port.connect(output_port)
|
||||
. = TRUE
|
||||
if("remove_connection")
|
||||
var/component_id = text2num(params["component_id"])
|
||||
var/is_input = params["is_input"]
|
||||
var/port_id = text2num(params["port_id"])
|
||||
|
||||
if(!WITHIN_RANGE(component_id, attached_components))
|
||||
return
|
||||
var/obj/item/circuit_component/component = attached_components[component_id]
|
||||
|
||||
var/list/port_table
|
||||
if(is_input)
|
||||
port_table = component.input_ports
|
||||
else
|
||||
port_table = component.output_ports
|
||||
|
||||
if(!WITHIN_RANGE(port_id, port_table))
|
||||
return
|
||||
|
||||
var/datum/port/port = port_table[port_id]
|
||||
port.disconnect_all()
|
||||
. = TRUE
|
||||
if("detach_component")
|
||||
var/component_id = text2num(params["component_id"])
|
||||
if(!WITHIN_RANGE(component_id, attached_components))
|
||||
return
|
||||
var/obj/item/circuit_component/component = attached_components[component_id]
|
||||
if(!component.removable)
|
||||
return
|
||||
component.disconnect()
|
||||
remove_component(component)
|
||||
if(component.loc == src)
|
||||
usr.put_in_hands(component)
|
||||
. = TRUE
|
||||
if("set_component_coordinates")
|
||||
var/component_id = text2num(params["component_id"])
|
||||
if(!WITHIN_RANGE(component_id, attached_components))
|
||||
return
|
||||
var/obj/item/circuit_component/component = attached_components[component_id]
|
||||
component.rel_x = min(max(-COMPONENT_MAX_POS, text2num(params["rel_x"])), COMPONENT_MAX_POS)
|
||||
component.rel_y = min(max(-COMPONENT_MAX_POS, text2num(params["rel_y"])), COMPONENT_MAX_POS)
|
||||
. = TRUE
|
||||
if("set_component_input")
|
||||
var/component_id = text2num(params["component_id"])
|
||||
var/port_id = text2num(params["port_id"])
|
||||
if(!WITHIN_RANGE(component_id, attached_components))
|
||||
return
|
||||
var/obj/item/circuit_component/component = attached_components[component_id]
|
||||
if(!WITHIN_RANGE(port_id, component.input_ports))
|
||||
return
|
||||
var/datum/port/input/port = component.input_ports[port_id]
|
||||
|
||||
if(params["set_null"])
|
||||
port.set_input(null)
|
||||
return TRUE
|
||||
|
||||
if(params["marked_atom"])
|
||||
if(port.datatype != PORT_TYPE_ATOM && port.datatype != PORT_TYPE_ANY)
|
||||
return
|
||||
var/obj/item/multitool/circuit/marker = usr.get_active_held_item()
|
||||
if(!istype(marker))
|
||||
var/client/user = usr.client
|
||||
if(!check_rights_for(user, R_VAREDIT))
|
||||
return TRUE
|
||||
var/atom/marked_atom = user.holder.marked_datum
|
||||
if(!marked_atom)
|
||||
return TRUE
|
||||
port.set_input(marked_atom)
|
||||
balloon_alert(usr, "updated [port.name]'s value to marked object.")
|
||||
return TRUE
|
||||
if(!marker.marked_atom)
|
||||
port.set_input(null)
|
||||
marker.say("Cleared port ('[port.name]')'s value.")
|
||||
return TRUE
|
||||
marker.say("Updated port ('[port.name]')'s value to the marked entity.")
|
||||
port.set_input(marker.marked_atom)
|
||||
return TRUE
|
||||
|
||||
var/user_input = port.handle_manual_input(usr, params["input"])
|
||||
if(isnull(user_input))
|
||||
return TRUE
|
||||
port.set_input(user_input)
|
||||
. = TRUE
|
||||
if("get_component_value")
|
||||
var/component_id = text2num(params["component_id"])
|
||||
var/port_id = text2num(params["port_id"])
|
||||
if(!WITHIN_RANGE(component_id, attached_components))
|
||||
return
|
||||
var/obj/item/circuit_component/component = attached_components[component_id]
|
||||
if(!WITHIN_RANGE(port_id, component.output_ports))
|
||||
return
|
||||
|
||||
var/datum/port/output/port = component.output_ports[port_id]
|
||||
var/value = port.value
|
||||
if(isatom(value))
|
||||
value = PORT_TYPE_ATOM
|
||||
else if(isnull(value))
|
||||
value = "null"
|
||||
var/string_form = copytext("[value]", 1, PORT_MAX_STRING_DISPLAY)
|
||||
if(length(string_form) >= PORT_MAX_STRING_DISPLAY-1)
|
||||
string_form += "..."
|
||||
balloon_alert(usr, "[port.name] value: [string_form]")
|
||||
. = TRUE
|
||||
if("set_display_name")
|
||||
var/new_name = params["display_name"]
|
||||
|
||||
if(new_name)
|
||||
set_display_name(strip_html(params["display_name"], label_max_length))
|
||||
else
|
||||
set_display_name("")
|
||||
|
||||
if(shell)
|
||||
if(display_name != "")
|
||||
shell.name = "[initial(shell.name)] ([display_name])"
|
||||
else
|
||||
shell.name = initial(shell.name)
|
||||
|
||||
. = TRUE
|
||||
if("set_examined_component")
|
||||
var/component_id = text2num(params["component_id"])
|
||||
if(!WITHIN_RANGE(component_id, attached_components))
|
||||
return
|
||||
examined_component = WEAKREF(attached_components[component_id])
|
||||
examined_rel_x = text2num(params["x"])
|
||||
examined_rel_y = text2num(params["y"])
|
||||
. = TRUE
|
||||
if("remove_examined_component")
|
||||
examined_component = null
|
||||
. = TRUE
|
||||
if("save_circuit")
|
||||
return attempt_save_to(usr.client)
|
||||
if("add_variable")
|
||||
var/variable_identifier = trim(copytext(params["variable_name"], 1, PORT_MAX_NAME_LENGTH))
|
||||
if(variable_identifier in circuit_variables)
|
||||
return TRUE
|
||||
if(variable_identifier == "")
|
||||
return TRUE
|
||||
var/variable_datatype = params["variable_datatype"]
|
||||
if(!(variable_datatype in GLOB.wiremod_basic_types))
|
||||
return
|
||||
|
||||
circuit_variables[variable_identifier] = new /datum/circuit_variable(variable_identifier, variable_datatype)
|
||||
. = TRUE
|
||||
if("remove_variable")
|
||||
var/variable_identifier = params["variable_name"]
|
||||
if(!(variable_identifier in circuit_variables))
|
||||
return
|
||||
var/datum/circuit_variable/variable = circuit_variables[variable_identifier]
|
||||
if(!variable)
|
||||
return
|
||||
circuit_variables -= variable_identifier
|
||||
qdel(variable)
|
||||
. = TRUE
|
||||
if("add_setter_or_getter")
|
||||
if(setter_and_getter_count >= max_setters_and_getters)
|
||||
balloon_alert(usr, "setter and getter count at maximum capacity")
|
||||
return
|
||||
var/designated_type = /obj/item/circuit_component/getter
|
||||
if(params["is_setter"])
|
||||
designated_type = /obj/item/circuit_component/setter
|
||||
var/obj/item/circuit_component/component = new designated_type(src)
|
||||
if(!add_component(component, usr))
|
||||
qdel(component)
|
||||
return
|
||||
RegisterSignal(component, COMSIG_CIRCUIT_COMPONENT_REMOVED, .proc/clear_setter_or_getter)
|
||||
setter_and_getter_count++
|
||||
if("move_screen")
|
||||
screen_x = text2num(params["screen_x"])
|
||||
screen_y = text2num(params["screen_y"])
|
||||
|
||||
/obj/item/integrated_circuit/proc/clear_setter_or_getter(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
// This'll also be called in the Destroy() override of /obj/item/circuit_component
|
||||
if(!QDELING(source))
|
||||
qdel(source)
|
||||
setter_and_getter_count--
|
||||
|
||||
/obj/item/integrated_circuit/proc/on_atom_usb_cable_try_attach(datum/source, obj/item/usb_cable/usb_cable, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
usb_cable.balloon_alert(user, "circuit needs to be in a compatible shell")
|
||||
return COMSIG_CANCEL_USB_CABLE_ATTACK
|
||||
|
||||
#undef WITHIN_RANGE
|
||||
|
||||
/// Sets the display name that appears on the shell.
|
||||
/obj/item/integrated_circuit/proc/set_display_name(new_name)
|
||||
display_name = new_name
|
||||
|
||||
/**
|
||||
* Returns the creator of the integrated circuit. Used in admin messages and other related things.
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/get_creator_admin()
|
||||
return get_creator(include_link = TRUE)
|
||||
|
||||
/**
|
||||
* Returns the creator of the integrated circuit. Used in admin logs and other related things.
|
||||
*/
|
||||
/obj/item/integrated_circuit/proc/get_creator(include_link = FALSE)
|
||||
var/datum/mind/inserter
|
||||
if(inserter_mind)
|
||||
inserter = inserter_mind.resolve()
|
||||
|
||||
var/obj/item/card/id/id_card
|
||||
if(owner_id)
|
||||
id_card = owner_id.resolve()
|
||||
|
||||
return "[src] (Shell: [shell || "*null*"], Inserter: [key_name(inserter, include_link)], Owner ID: [id_card?.name || "*null*"])"
|
||||
|
||||
/// Attempts to save a circuit to a given client
|
||||
/obj/item/integrated_circuit/proc/attempt_save_to(client/saver)
|
||||
if(!check_rights_for(saver, R_VAREDIT))
|
||||
return FALSE
|
||||
var/temp_file = file("data/CircuitDownloadTempFile")
|
||||
fdel(temp_file)
|
||||
WRITE_FILE(temp_file, convert_to_json())
|
||||
DIRECT_OUTPUT(saver, ftp(temp_file, "[display_name || "circuit"].json"))
|
||||
return TRUE
|
||||
@@ -0,0 +1,60 @@
|
||||
/obj/item/multitool/circuit
|
||||
name = "circuit multitool"
|
||||
desc = "A circuit multitool. Used to mark entities which can then be uploaded to components by pressing the upload button on a port. \
|
||||
Acts as a normal multitool otherwise. Use in hand to clear marked entity so that you can mark another entity."
|
||||
icon_state = "multitool_circuit"
|
||||
|
||||
/// The marked atom of this multitool
|
||||
var/atom/marked_atom
|
||||
|
||||
/obj/item/multitool/circuit/Destroy()
|
||||
marked_atom = null
|
||||
return ..()
|
||||
|
||||
/obj/item/multitool/circuit/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("It has [marked_atom? "a" : "no"] marked entity registered.")
|
||||
|
||||
/obj/item/multitool/circuit/attack_self(mob/user, modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(!marked_atom)
|
||||
return
|
||||
|
||||
say("Cleared marked targets.")
|
||||
clear_marked_atom()
|
||||
return TRUE
|
||||
|
||||
/obj/item/multitool/circuit/melee_attack_chain(mob/user, atom/target, params)
|
||||
var/is_right_clicking = LAZYACCESS(params2list(params), RIGHT_CLICK)
|
||||
|
||||
if(marked_atom || !user.Adjacent(target) || is_right_clicking)
|
||||
return ..()
|
||||
|
||||
say("Marked [target].")
|
||||
marked_atom = target
|
||||
RegisterSignal(marked_atom, COMSIG_PARENT_QDELETING, .proc/cleanup_marked_atom)
|
||||
update_icon()
|
||||
flick("multitool_circuit_flick", src)
|
||||
playsound(src.loc, 'sound/misc/compiler-stage2.ogg', 30, TRUE)
|
||||
return TRUE
|
||||
|
||||
/obj/item/multitool/circuit/update_overlays()
|
||||
. = ..()
|
||||
cut_overlays()
|
||||
if(marked_atom)
|
||||
. += "marked_overlay"
|
||||
|
||||
/// Clears the current marked atom
|
||||
/obj/item/multitool/circuit/proc/clear_marked_atom()
|
||||
if(!marked_atom)
|
||||
return
|
||||
UnregisterSignal(marked_atom, COMSIG_PARENT_QDELETING)
|
||||
marked_atom = null
|
||||
update_icon()
|
||||
|
||||
/obj/item/multitool/circuit/proc/cleanup_marked_atom(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
if(source == marked_atom)
|
||||
clear_marked_atom()
|
||||
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* # 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)
|
||||
@@ -0,0 +1,123 @@
|
||||
/// A cable that can connect integrated circuits to anything with a USB port, such as computers and machines.
|
||||
/obj/item/usb_cable
|
||||
name = "usb cable"
|
||||
desc = "A cable that can connect integrated circuits to anything with a USB port, such as computers and machines."
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "usb_cable"
|
||||
inhand_icon_state = "coil"
|
||||
base_icon_state = "coil"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
custom_materials = list(/datum/material/iron = 75)
|
||||
|
||||
/// The currently connected circuit
|
||||
var/obj/item/integrated_circuit/attached_circuit
|
||||
|
||||
/obj/item/usb_cable/Destroy()
|
||||
attached_circuit = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/usb_cable/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
|
||||
/obj/item/usb_cable/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
if (!isnull(attached_circuit))
|
||||
. += span_notice("It is attached to [attached_circuit.shell || attached_circuit].")
|
||||
|
||||
// Look, I'm not happy about this either, but moving an object doesn't call Moved if it's inside something else.
|
||||
// There's good reason for this, but there's no element or similar yet to track it as far as I know.
|
||||
// SSobj runs infrequently, this is only ran while there's an attached circuit, its performance cost is negligible.
|
||||
/obj/item/usb_cable/process(delta_time)
|
||||
if (!check_in_range())
|
||||
return PROCESS_KILL
|
||||
|
||||
/obj/item/usb_cable/pre_attack(atom/target, mob/living/user, params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
if (prob(1))
|
||||
balloon_alert(user, "wrong way, god damnit")
|
||||
return TRUE
|
||||
|
||||
var/signal_result = SEND_SIGNAL(target, COMSIG_ATOM_USB_CABLE_TRY_ATTACH, src, user)
|
||||
|
||||
var/last_attached_circuit = attached_circuit
|
||||
if (signal_result & COMSIG_USB_CABLE_CONNECTED_TO_CIRCUIT)
|
||||
if (isnull(attached_circuit))
|
||||
CRASH("Producers of COMSIG_USB_CABLE_CONNECTED_TO_CIRCUIT must set attached_circuit")
|
||||
balloon_alert(user, "connected to circuit\nconnect to a port")
|
||||
|
||||
playsound(src, 'sound/machines/pda_button1.ogg', 20, TRUE)
|
||||
|
||||
if (last_attached_circuit != attached_circuit)
|
||||
if (!isnull(last_attached_circuit))
|
||||
unregister_circuit_signals(last_attached_circuit)
|
||||
register_circuit_signals()
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
return TRUE
|
||||
|
||||
if (signal_result & COMSIG_USB_CABLE_ATTACHED)
|
||||
// Short messages are better to read
|
||||
var/connection_description = "port"
|
||||
if (istype(target, /obj/machinery/computer))
|
||||
connection_description = "computer"
|
||||
else if (ismachinery(target))
|
||||
connection_description = "machine"
|
||||
|
||||
balloon_alert(user, "connected to [connection_description]")
|
||||
playsound(src, 'sound/items/screwdriver2.ogg', 20, TRUE)
|
||||
|
||||
return TRUE
|
||||
|
||||
if (signal_result & COMSIG_CANCEL_USB_CABLE_ATTACK)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/obj/item/usb_cable/suicide_act(mob/user)
|
||||
user.visible_message(span_suicide("[user] is wrapping [src] around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
return OXYLOSS
|
||||
|
||||
/obj/item/usb_cable/proc/register_circuit_signals()
|
||||
RegisterSignal(attached_circuit, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
RegisterSignal(attached_circuit, COMSIG_PARENT_QDELETING, .proc/on_circuit_qdeling)
|
||||
RegisterSignal(attached_circuit.shell, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
|
||||
/obj/item/usb_cable/proc/unregister_circuit_signals(obj/item/integrated_circuit/old_circuit)
|
||||
UnregisterSignal(attached_circuit, list(
|
||||
COMSIG_MOVABLE_MOVED,
|
||||
COMSIG_PARENT_QDELETING,
|
||||
))
|
||||
|
||||
UnregisterSignal(attached_circuit.shell, COMSIG_MOVABLE_MOVED)
|
||||
|
||||
/obj/item/usb_cable/proc/on_moved()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
check_in_range()
|
||||
|
||||
/obj/item/usb_cable/proc/check_in_range()
|
||||
if (isnull(attached_circuit))
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return FALSE
|
||||
|
||||
if (!IN_GIVEN_RANGE(attached_circuit, src, USB_CABLE_MAX_RANGE))
|
||||
balloon_alert_to_viewers("detached, too far away")
|
||||
unregister_circuit_signals(attached_circuit)
|
||||
attached_circuit = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/usb_cable/proc/on_circuit_qdeling()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
attached_circuit = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* A circuit variable that holds the name, the datatype and the colour of the variable (taken from the datatype).
|
||||
*
|
||||
* Used in integrated circuits for setter and getter circuit components.
|
||||
*/
|
||||
/datum/circuit_variable
|
||||
/// The display name of the circuit variable
|
||||
var/name
|
||||
|
||||
/// The datatype of the circuit variable. Used by the setter and getter circuit components
|
||||
var/datatype
|
||||
|
||||
/// The colour that appears in the UI. The value is set to the datatype's matching colour
|
||||
var/color
|
||||
|
||||
/// The current value held by the variable.
|
||||
var/value
|
||||
|
||||
/// The components that are currently listening. Triggers them when the value is updated.
|
||||
var/list/obj/item/circuit_component/listeners
|
||||
|
||||
/datum/circuit_variable/New(name, datatype)
|
||||
. = ..()
|
||||
src.name = name
|
||||
src.datatype = datatype
|
||||
|
||||
var/datum/circuit_datatype/circuit_datatype = GLOB.circuit_datatypes[datatype]
|
||||
|
||||
src.listeners = list()
|
||||
src.color = circuit_datatype.color
|
||||
|
||||
|
||||
/datum/circuit_variable/Destroy(force, ...)
|
||||
listeners = null
|
||||
return ..()
|
||||
|
||||
/// Sets the value of the circuit component and triggers the appropriate listeners
|
||||
/datum/circuit_variable/proc/set_value(new_value)
|
||||
value = new_value
|
||||
for(var/obj/item/circuit_component/component as anything in listeners)
|
||||
TRIGGER_CIRCUIT_COMPONENT(component, null)
|
||||
|
||||
/// Adds a listener to receive inputs when the variable has a value that is set.
|
||||
/datum/circuit_variable/proc/add_listener(obj/item/circuit_component/to_add)
|
||||
listeners += to_add
|
||||
|
||||
/// Removes a listener to receive inputs when the variable has a value that is set. Listener will usually clean themselves up
|
||||
/datum/circuit_variable/proc/remove_listener(obj/item/circuit_component/to_remove)
|
||||
listeners -= to_remove
|
||||
@@ -0,0 +1,10 @@
|
||||
/datum/circuit_datatype/any
|
||||
datatype = PORT_TYPE_ANY
|
||||
color = "blue"
|
||||
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
|
||||
|
||||
/datum/circuit_datatype/any/can_receive_from_datatype(datatype_to_check)
|
||||
return TRUE
|
||||
|
||||
/datum/circuit_datatype/any/handle_manual_input(datum/port/input/port, mob/user, user_input)
|
||||
return text2num(user_input) || user_input
|
||||
@@ -0,0 +1,9 @@
|
||||
// This file is for types that do not have any special conversion behaviour
|
||||
|
||||
/datum/circuit_datatype/list_type
|
||||
datatype = PORT_TYPE_LIST
|
||||
color = "white"
|
||||
|
||||
/datum/circuit_datatype/table
|
||||
datatype = PORT_TYPE_TABLE
|
||||
color = "grey"
|
||||
@@ -0,0 +1,10 @@
|
||||
/datum/circuit_datatype/entity
|
||||
datatype = PORT_TYPE_ATOM
|
||||
color = "purple"
|
||||
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
|
||||
|
||||
/datum/circuit_datatype/entity/convert_value(datum/port/port, value_to_convert)
|
||||
var/atom/object = value_to_convert
|
||||
if(QDELETED(object))
|
||||
return null
|
||||
return object
|
||||
@@ -0,0 +1,14 @@
|
||||
/datum/circuit_datatype/number
|
||||
datatype = PORT_TYPE_NUMBER
|
||||
color = "green"
|
||||
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
|
||||
|
||||
/datum/circuit_datatype/number/can_receive_from_datatype(datatype_to_check)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
return datatype_to_check == PORT_TYPE_NUMBER
|
||||
|
||||
/datum/circuit_datatype/number/handle_manual_input(datum/port/input/port, mob/user, user_input)
|
||||
return text2num(user_input)
|
||||
@@ -0,0 +1,33 @@
|
||||
/datum/port/input/option
|
||||
var/list/possible_options
|
||||
|
||||
/datum/port/input/option/New(obj/item/circuit_component/to_connect, name, datatype, trigger, default, possible_options)
|
||||
. = ..()
|
||||
src.possible_options = possible_options
|
||||
set_value(default, force = TRUE)
|
||||
|
||||
/datum/circuit_datatype/option
|
||||
datatype = PORT_TYPE_OPTION
|
||||
color = "violet"
|
||||
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
|
||||
|
||||
/datum/circuit_datatype/option/is_compatible(datum/port/gained_port)
|
||||
return istype(gained_port, /datum/port/input/option)
|
||||
|
||||
/datum/circuit_datatype/option/can_receive_from_datatype(datatype_to_check)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
return datatype_to_check == PORT_TYPE_STRING
|
||||
|
||||
/datum/circuit_datatype/option/convert_value(datum/port/input/option/port, value_to_convert)
|
||||
if(!port.possible_options)
|
||||
return null
|
||||
|
||||
if(value_to_convert in port.possible_options)
|
||||
return value_to_convert
|
||||
return port.possible_options[1]
|
||||
|
||||
/datum/circuit_datatype/option/datatype_ui_data(datum/port/input/option/port)
|
||||
return port.possible_options
|
||||
@@ -0,0 +1,17 @@
|
||||
/datum/circuit_datatype/signal
|
||||
datatype = PORT_TYPE_SIGNAL
|
||||
color = "teal"
|
||||
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT|DATATYPE_FLAG_AVOID_VALUE_UPDATE
|
||||
|
||||
/datum/circuit_datatype/signal/can_receive_from_datatype(datatype_to_check)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
return datatype_to_check == PORT_TYPE_NUMBER
|
||||
|
||||
/datum/circuit_datatype/signal/handle_manual_input(datum/port/input/port, mob/user, user_input)
|
||||
var/atom/parent = port.connected_component
|
||||
if(parent)
|
||||
parent.balloon_alert(user, "triggered [port.name]")
|
||||
return COMPONENT_SIGNAL
|
||||
@@ -0,0 +1,17 @@
|
||||
/datum/circuit_datatype/string
|
||||
datatype = PORT_TYPE_STRING
|
||||
color = "orange"
|
||||
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
|
||||
|
||||
/datum/circuit_datatype/string/can_receive_from_datatype(datatype_to_check)
|
||||
return TRUE
|
||||
|
||||
/datum/circuit_datatype/string/convert_value(datum/port/port, value_to_convert)
|
||||
if(isnull(value_to_convert))
|
||||
return
|
||||
|
||||
// So that they can't easily get the name like this.
|
||||
if(isatom(value_to_convert))
|
||||
return PORT_TYPE_ATOM
|
||||
else
|
||||
return copytext("[value_to_convert]", 1, PORT_MAX_STRING_LENGTH)
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* # Hello World preset
|
||||
*
|
||||
* Says "Hello World" when triggered. Needs to be wired up and connected first.
|
||||
*/
|
||||
/obj/item/integrated_circuit/loaded/hello_world
|
||||
|
||||
/obj/item/integrated_circuit/loaded/hello_world/Initialize()
|
||||
. = ..()
|
||||
var/obj/item/circuit_component/speech/speech = new()
|
||||
add_component(speech)
|
||||
|
||||
speech.message.set_input("Hello World")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* # Speech Relay preset
|
||||
*
|
||||
* Acts like poly. Says whatever it hears.
|
||||
*/
|
||||
/obj/item/integrated_circuit/loaded/speech_relay
|
||||
|
||||
/obj/item/integrated_circuit/loaded/speech_relay/Initialize()
|
||||
. = ..()
|
||||
var/obj/item/circuit_component/hear/hear = new()
|
||||
add_component(hear)
|
||||
hear.rel_x = 100
|
||||
hear.rel_y = 200
|
||||
|
||||
var/obj/item/circuit_component/speech/speech = new()
|
||||
add_component(speech)
|
||||
speech.rel_x = 400
|
||||
speech.rel_y = 200
|
||||
|
||||
speech.message.connect(hear.message_port)
|
||||
@@ -0,0 +1,133 @@
|
||||
/datum/wires/airlock/shell
|
||||
holder_type = /obj/machinery/door/airlock/shell
|
||||
proper_name = "Circuit Airlock"
|
||||
|
||||
/datum/wires/airlock/shell/on_cut(wire, mend)
|
||||
// Don't allow them to re-enable autoclose.
|
||||
if(wire == WIRE_TIMING)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/door/airlock/shell
|
||||
name = "circuit airlock"
|
||||
autoclose = FALSE
|
||||
|
||||
/obj/machinery/door/airlock/shell/Initialize()
|
||||
. = ..()
|
||||
AddComponent( \
|
||||
/datum/component/shell, \
|
||||
unremovable_circuit_components = list(new /obj/item/circuit_component/airlock), \
|
||||
capacity = SHELL_CAPACITY_LARGE, \
|
||||
shell_flags = SHELL_FLAG_ALLOW_FAILURE_ACTION \
|
||||
)
|
||||
|
||||
/obj/machinery/door/airlock/shell/check_access(obj/item/I)
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/door/airlock/shell/canAIControl(mob/user)
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/door/airlock/shell/canAIHack(mob/user)
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/door/airlock/shell/set_wires()
|
||||
return new /datum/wires/airlock/shell(src)
|
||||
|
||||
/obj/item/circuit_component/airlock
|
||||
display_name = "Airlock"
|
||||
desc = "The general interface with an airlock. Includes general statuses of the airlock"
|
||||
|
||||
/// Called when attack_hand is called on the shell.
|
||||
var/obj/machinery/door/airlock/attached_airlock
|
||||
|
||||
/// Bolts the airlock (if possible)
|
||||
var/datum/port/input/bolt
|
||||
/// Unbolts the airlock (if possible)
|
||||
var/datum/port/input/unbolt
|
||||
/// Opens the airlock (if possible)
|
||||
var/datum/port/input/open
|
||||
/// Closes the airlock (if possible)
|
||||
var/datum/port/input/close
|
||||
|
||||
/// Contains whether the airlock is open or not
|
||||
var/datum/port/output/is_open
|
||||
/// Contains whether the airlock is bolted or not
|
||||
var/datum/port/output/is_bolted
|
||||
|
||||
/// Called when the airlock is opened.
|
||||
var/datum/port/output/opened
|
||||
/// Called when the airlock is closed
|
||||
var/datum/port/output/closed
|
||||
|
||||
/// Called when the airlock is bolted
|
||||
var/datum/port/output/bolted
|
||||
/// Called when the airlock is unbolted
|
||||
var/datum/port/output/unbolted
|
||||
|
||||
/obj/item/circuit_component/airlock/Initialize()
|
||||
. = ..()
|
||||
// Input Signals
|
||||
bolt = add_input_port("Bolt", PORT_TYPE_SIGNAL)
|
||||
unbolt = add_input_port("Unbolt", PORT_TYPE_SIGNAL)
|
||||
open = add_input_port("Open", PORT_TYPE_SIGNAL)
|
||||
close = add_input_port("Close", PORT_TYPE_SIGNAL)
|
||||
// States
|
||||
is_open = add_output_port("Is Open", PORT_TYPE_NUMBER)
|
||||
is_bolted = add_output_port("Is Bolted", PORT_TYPE_NUMBER)
|
||||
// Output Signals
|
||||
opened = add_output_port("Opened", PORT_TYPE_SIGNAL)
|
||||
closed = add_output_port("Closed", PORT_TYPE_SIGNAL)
|
||||
bolted = add_output_port("Bolted", PORT_TYPE_SIGNAL)
|
||||
unbolted = add_output_port("Unbolted", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/airlock/register_shell(atom/movable/shell)
|
||||
. = ..()
|
||||
if(istype(shell, /obj/machinery/door/airlock))
|
||||
attached_airlock = shell
|
||||
RegisterSignal(shell, COMSIG_AIRLOCK_SET_BOLT, .proc/on_airlock_set_bolted)
|
||||
RegisterSignal(shell, COMSIG_AIRLOCK_OPEN, .proc/on_airlock_open)
|
||||
RegisterSignal(shell, COMSIG_AIRLOCK_CLOSE, .proc/on_airlock_closed)
|
||||
|
||||
/obj/item/circuit_component/airlock/unregister_shell(atom/movable/shell)
|
||||
attached_airlock = null
|
||||
UnregisterSignal(shell, list(
|
||||
COMSIG_AIRLOCK_SET_BOLT,
|
||||
COMSIG_AIRLOCK_OPEN,
|
||||
COMSIG_AIRLOCK_CLOSE,
|
||||
))
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/airlock/proc/on_airlock_set_bolted(datum/source, should_bolt)
|
||||
SIGNAL_HANDLER
|
||||
is_bolted.set_output(should_bolt)
|
||||
if(should_bolt)
|
||||
bolted.set_output(COMPONENT_SIGNAL)
|
||||
else
|
||||
unbolted.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/airlock/proc/on_airlock_open(datum/source, force)
|
||||
SIGNAL_HANDLER
|
||||
is_open.set_output(TRUE)
|
||||
opened.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/airlock/proc/on_airlock_closed(datum/source, forced)
|
||||
SIGNAL_HANDLER
|
||||
is_open.set_output(FALSE)
|
||||
closed.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/airlock/input_received(datum/port/input/port)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
if(!attached_airlock)
|
||||
return
|
||||
|
||||
if(COMPONENT_TRIGGERED_BY(bolt, port))
|
||||
attached_airlock.bolt()
|
||||
if(COMPONENT_TRIGGERED_BY(unbolt, port))
|
||||
attached_airlock.unbolt()
|
||||
if(COMPONENT_TRIGGERED_BY(open, port) && attached_airlock.density)
|
||||
INVOKE_ASYNC(attached_airlock, /obj/machinery/door/airlock.proc/open)
|
||||
if(COMPONENT_TRIGGERED_BY(close, port) && !attached_airlock.density)
|
||||
INVOKE_ASYNC(attached_airlock, /obj/machinery/door/airlock.proc/close)
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* # Bot
|
||||
*
|
||||
* Immobile (but not dense) shells that can interact with world.
|
||||
*/
|
||||
/obj/structure/bot
|
||||
name = "bot"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_medium_box"
|
||||
|
||||
density = FALSE
|
||||
light_system = MOVABLE_LIGHT
|
||||
light_on = FALSE
|
||||
|
||||
/obj/structure/bot/Initialize()
|
||||
. = ..()
|
||||
AddComponent( \
|
||||
/datum/component/shell, \
|
||||
unremovable_circuit_components = list(new /obj/item/circuit_component/bot), \
|
||||
capacity = SHELL_CAPACITY_LARGE, \
|
||||
shell_flags = SHELL_FLAG_USB_PORT, \
|
||||
)
|
||||
|
||||
/obj/item/circuit_component/bot
|
||||
display_name = "Bot"
|
||||
desc = "Triggers when someone interacts with the bot."
|
||||
|
||||
/// Called when attack_hand is called on the shell.
|
||||
var/datum/port/output/signal
|
||||
/// The user who used the bot
|
||||
var/datum/port/output/entity
|
||||
|
||||
/obj/item/circuit_component/bot/Initialize()
|
||||
. = ..()
|
||||
entity = add_output_port("User", PORT_TYPE_ATOM)
|
||||
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/bot/Destroy()
|
||||
signal = null
|
||||
entity = null
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/bot/register_shell(atom/movable/shell)
|
||||
RegisterSignal(shell, COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
|
||||
|
||||
/obj/item/circuit_component/bot/unregister_shell(atom/movable/shell)
|
||||
UnregisterSignal(shell, COMSIG_ATOM_ATTACK_HAND)
|
||||
|
||||
/obj/item/circuit_component/bot/proc/on_attack_hand(atom/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
source.balloon_alert(user, "pushed button")
|
||||
playsound(source, get_sfx("terminal_type"), 25, FALSE)
|
||||
entity.set_output(user)
|
||||
signal.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
/obj/item/organ/cyberimp/bci
|
||||
name = "brain-computer interface"
|
||||
desc = "An implant that can be placed in a user's head to control circuits using their brain."
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "bci"
|
||||
zone = BODY_ZONE_HEAD
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
/obj/item/organ/cyberimp/bci/Initialize()
|
||||
. = ..()
|
||||
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/bci_core,
|
||||
new /obj/item/circuit_component/bci_action(null, "One"),
|
||||
new /obj/item/circuit_component/bci_action(null, "Two"),
|
||||
new /obj/item/circuit_component/bci_action(null, "Three"),
|
||||
), SHELL_CAPACITY_SMALL)
|
||||
|
||||
/obj/item/organ/cyberimp/bci/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
|
||||
. = ..()
|
||||
|
||||
// Organs are put in nullspace, but this breaks circuit interactions
|
||||
forceMove(reciever)
|
||||
|
||||
/obj/item/organ/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced)
|
||||
if (owner)
|
||||
// Otherwise say_dead will be called.
|
||||
// It's intentional that a circuit for a dead person does not speak from the shell.
|
||||
if (owner.stat == DEAD)
|
||||
return
|
||||
|
||||
owner.say(message, forced = "circuit speech")
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/bci_action
|
||||
display_name = "BCI Action"
|
||||
desc = "Represents an action the user can take when implanted with the brain-computer interface."
|
||||
required_shells = list(/obj/item/organ/cyberimp/bci)
|
||||
|
||||
/// The icon of the button
|
||||
var/datum/port/input/option/icon_options
|
||||
|
||||
/// The name to use for the button
|
||||
var/datum/port/input/button_name
|
||||
|
||||
/// Called when the user presses the button
|
||||
var/datum/port/output/signal
|
||||
|
||||
/// A reference to the action button itself
|
||||
var/datum/action/innate/bci_action/bci_action
|
||||
|
||||
/obj/item/circuit_component/bci_action/Initialize(mapload, default_icon)
|
||||
. = ..()
|
||||
|
||||
if (!isnull(default_icon))
|
||||
icon_options.set_input(default_icon)
|
||||
|
||||
button_name = add_input_port("Name", PORT_TYPE_STRING)
|
||||
|
||||
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/bci_action/Destroy()
|
||||
QDEL_NULL(bci_action)
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/bci_action/populate_options()
|
||||
var/static/action_options = list(
|
||||
"Blank",
|
||||
|
||||
"One",
|
||||
"Two",
|
||||
"Three",
|
||||
"Four",
|
||||
"Five",
|
||||
|
||||
"Blood",
|
||||
"Bomb",
|
||||
"Brain",
|
||||
"Brain Damage",
|
||||
"Cross",
|
||||
"Electricity",
|
||||
"Exclamation",
|
||||
"Heart",
|
||||
"Id",
|
||||
"Info",
|
||||
"Injection",
|
||||
"Magnetism",
|
||||
"Minus",
|
||||
"Network",
|
||||
"Plus",
|
||||
"Power",
|
||||
"Question",
|
||||
"Radioactive",
|
||||
"Reaction",
|
||||
"Repair",
|
||||
"Say",
|
||||
"Scan",
|
||||
"Shield",
|
||||
"Skull",
|
||||
"Sleep",
|
||||
"Wireless",
|
||||
)
|
||||
|
||||
icon_options = add_option_port("Icon", action_options)
|
||||
|
||||
/obj/item/circuit_component/bci_action/register_shell(atom/movable/shell)
|
||||
var/obj/item/organ/cyberimp/bci/bci = shell
|
||||
|
||||
bci_action = new(src)
|
||||
update_action()
|
||||
|
||||
bci.actions += list(bci_action)
|
||||
|
||||
/obj/item/circuit_component/bci_action/unregister_shell(atom/movable/shell)
|
||||
var/obj/item/organ/cyberimp/bci/bci = shell
|
||||
|
||||
bci.actions -= bci_action
|
||||
QDEL_NULL(bci_action)
|
||||
|
||||
/obj/item/circuit_component/bci_action/input_received(datum/port/input/port)
|
||||
. = ..()
|
||||
|
||||
if (.)
|
||||
return
|
||||
|
||||
if (!isnull(bci_action))
|
||||
update_action()
|
||||
|
||||
/obj/item/circuit_component/bci_action/proc/update_action()
|
||||
bci_action.name = button_name.value
|
||||
bci_action.button_icon_state = "bci_[replacetextEx(lowertext(icon_options.value), " ", "_")]"
|
||||
|
||||
/datum/action/innate/bci_action
|
||||
name = "Action"
|
||||
icon_icon = 'icons/mob/actions/actions_items.dmi'
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
button_icon_state = "bci_power"
|
||||
|
||||
var/obj/item/circuit_component/bci_action/circuit_component
|
||||
|
||||
/datum/action/innate/bci_action/New(obj/item/circuit_component/bci_action/circuit_component)
|
||||
..()
|
||||
|
||||
src.circuit_component = circuit_component
|
||||
|
||||
/datum/action/innate/bci_action/Destroy()
|
||||
circuit_component.bci_action = null
|
||||
circuit_component = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/bci_action/Activate()
|
||||
circuit_component.signal.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/bci_core
|
||||
display_name = "BCI Core"
|
||||
desc = "Controls the core operations of the BCI."
|
||||
|
||||
/// A reference to the action button to look at charge/get info
|
||||
var/datum/action/innate/bci_charge_action/charge_action
|
||||
|
||||
var/datum/port/input/message
|
||||
var/datum/port/input/send_message_signal
|
||||
|
||||
var/datum/port/output/user_port
|
||||
|
||||
var/datum/weakref/user
|
||||
|
||||
/obj/item/circuit_component/bci_core/Initialize()
|
||||
. = ..()
|
||||
|
||||
message = add_input_port("Message", PORT_TYPE_STRING)
|
||||
send_message_signal = add_input_port("Send Message", PORT_TYPE_SIGNAL)
|
||||
|
||||
user_port = add_output_port("User", PORT_TYPE_ATOM)
|
||||
|
||||
/obj/item/circuit_component/bci_core/Destroy()
|
||||
QDEL_NULL(charge_action)
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/bci_core/register_shell(atom/movable/shell)
|
||||
var/obj/item/organ/cyberimp/bci/bci = shell
|
||||
|
||||
charge_action = new(src)
|
||||
bci.actions += list(charge_action)
|
||||
|
||||
RegisterSignal(shell, COMSIG_ORGAN_IMPLANTED, .proc/on_organ_implanted)
|
||||
RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
|
||||
|
||||
/obj/item/circuit_component/bci_core/unregister_shell(atom/movable/shell)
|
||||
var/obj/item/organ/cyberimp/bci/bci = shell
|
||||
|
||||
bci.actions -= charge_action
|
||||
QDEL_NULL(charge_action)
|
||||
|
||||
UnregisterSignal(shell, list(
|
||||
COMSIG_ORGAN_IMPLANTED,
|
||||
COMSIG_ORGAN_REMOVED,
|
||||
))
|
||||
|
||||
/obj/item/circuit_component/bci_core/input_received(datum/port/input/port)
|
||||
. = ..()
|
||||
if (.)
|
||||
return .
|
||||
|
||||
if (COMPONENT_TRIGGERED_BY(send_message_signal, port))
|
||||
var/sent_message = trim(message.value)
|
||||
if (!sent_message)
|
||||
return
|
||||
|
||||
var/mob/living/carbon/resolved_owner = user?.resolve()
|
||||
if (isnull(resolved_owner))
|
||||
return
|
||||
|
||||
if (resolved_owner.stat == DEAD)
|
||||
return
|
||||
|
||||
to_chat(resolved_owner, "<i>You hear a strange, robotic voice in your head...</i> \"[span_robot("[html_encode(sent_message)]")]\"")
|
||||
|
||||
/obj/item/circuit_component/bci_core/proc/on_organ_implanted(datum/source, mob/living/carbon/owner)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
user_port.set_output(owner)
|
||||
user = WEAKREF(owner)
|
||||
|
||||
RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/on_examine)
|
||||
RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/on_borg_charge)
|
||||
RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_electrocute)
|
||||
|
||||
/obj/item/circuit_component/bci_core/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
user_port.set_output(null)
|
||||
user = null
|
||||
|
||||
UnregisterSignal(owner, list(
|
||||
COMSIG_PARENT_EXAMINE,
|
||||
COMSIG_PROCESS_BORGCHARGER_OCCUPANT,
|
||||
COMSIG_LIVING_ELECTROCUTE_ACT,
|
||||
))
|
||||
|
||||
/obj/item/circuit_component/bci_core/proc/on_borg_charge(datum/source, amount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (isnull(parent.cell))
|
||||
return
|
||||
|
||||
parent.cell.give(amount)
|
||||
|
||||
/obj/item/circuit_component/bci_core/proc/on_electrocute(datum/source, shock_damage, siemens_coefficient, flags)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (isnull(parent.cell))
|
||||
return
|
||||
|
||||
if (flags & SHOCK_ILLUSION)
|
||||
return
|
||||
|
||||
parent.cell.give(shock_damage * 2)
|
||||
to_chat(source, span_notice("You absorb some of the shock into your [parent.name]!"))
|
||||
|
||||
/obj/item/circuit_component/bci_core/proc/on_examine(datum/source, mob/mob, list/examine_text)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (isobserver(mob))
|
||||
examine_text += span_notice("[source.p_they(capitalized = TRUE)] [source.p_have()] <a href='?src=[REF(src)];open_bci=1'>\a [parent] implanted in [source.p_them()]</a>.")
|
||||
|
||||
/obj/item/circuit_component/bci_core/Topic(href, list/href_list)
|
||||
..()
|
||||
|
||||
if (!isobserver(usr))
|
||||
return
|
||||
|
||||
if (href_list["open_bci"])
|
||||
parent.attack_ghost(usr)
|
||||
|
||||
/datum/action/innate/bci_charge_action
|
||||
name = "Check BCI Charge"
|
||||
check_flags = NONE
|
||||
icon_icon = 'icons/obj/power.dmi'
|
||||
button_icon_state = "cell"
|
||||
|
||||
var/obj/item/circuit_component/bci_core/circuit_component
|
||||
|
||||
/datum/action/innate/bci_charge_action/New(obj/item/circuit_component/bci_core/circuit_component)
|
||||
..()
|
||||
|
||||
src.circuit_component = circuit_component
|
||||
|
||||
button.maptext_x = 2
|
||||
button.maptext_y = 0
|
||||
update_maptext()
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/datum/action/innate/bci_charge_action/Destroy()
|
||||
circuit_component.charge_action = null
|
||||
circuit_component = null
|
||||
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/bci_charge_action/Trigger()
|
||||
var/obj/item/stock_parts/cell/cell = circuit_component.parent.cell
|
||||
|
||||
if (isnull(cell))
|
||||
to_chat(owner, span_boldwarning("[circuit_component.parent] has no power cell."))
|
||||
else
|
||||
to_chat(owner, span_info("[circuit_component.parent]'s [cell.name] has <b>[cell.percent()]%</b> charge left."))
|
||||
to_chat(owner, span_info("You can recharge it by using a cyborg recharging station."))
|
||||
|
||||
/datum/action/innate/bci_charge_action/process(delta_time)
|
||||
update_maptext()
|
||||
|
||||
/datum/action/innate/bci_charge_action/proc/update_maptext()
|
||||
var/obj/item/stock_parts/cell/cell = circuit_component.parent.cell
|
||||
button.maptext = cell ? MAPTEXT("[cell.percent()]%") : ""
|
||||
|
||||
/obj/machinery/bci_implanter
|
||||
name = "brain-computer interface manipulation chamber"
|
||||
desc = "A machine that, when given a brain-computer interface, will implant it into an occupant. Otherwise, will remove any brain-computer interfaces they already have."
|
||||
circuit = /obj/item/circuitboard/machine/bci_implanter
|
||||
icon = 'icons/obj/machines/bci_implanter.dmi'
|
||||
icon_state = "bci_implanter"
|
||||
base_icon_state = "bci_implanter"
|
||||
layer = ABOVE_WINDOW_LAYER
|
||||
use_power = IDLE_POWER_USE
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
obj_flags = NO_BUILD // Becomes undense when the door is open
|
||||
idle_power_usage = 50
|
||||
active_power_usage = 300
|
||||
|
||||
var/busy = FALSE
|
||||
var/busy_icon_state
|
||||
var/locked = FALSE
|
||||
|
||||
var/datum/weakref/bci_to_implant
|
||||
|
||||
COOLDOWN_DECLARE(message_cooldown)
|
||||
|
||||
/obj/machinery/bci_implanter/Initialize()
|
||||
. = ..()
|
||||
occupant_typecache = typecacheof(/mob/living/carbon)
|
||||
|
||||
/obj/machinery/bci_implanter/Destroy()
|
||||
QDEL_NULL(bci_to_implant)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/bci_implanter/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
if (isnull(bci_to_implant?.resolve()))
|
||||
. += span_notice("There is no BCI inserted.")
|
||||
else
|
||||
. += span_notice("Right-click to remove current BCI.")
|
||||
|
||||
/obj/machinery/bci_implanter/proc/set_busy(status, working_icon)
|
||||
busy = status
|
||||
busy_icon_state = working_icon
|
||||
update_appearance()
|
||||
|
||||
/obj/machinery/bci_implanter/update_icon_state()
|
||||
if (occupant)
|
||||
icon_state = busy ? busy_icon_state : "[base_icon_state]_occupied"
|
||||
return ..()
|
||||
icon_state = "[base_icon_state][state_open ? "_open" : null]"
|
||||
return ..()
|
||||
|
||||
/obj/machinery/bci_implanter/update_overlays()
|
||||
var/list/overlays = ..()
|
||||
|
||||
if ((machine_stat & MAINT) || panel_open)
|
||||
overlays += "maint"
|
||||
return overlays
|
||||
|
||||
if (machine_stat & (NOPOWER|BROKEN))
|
||||
return overlays
|
||||
|
||||
if (busy || locked)
|
||||
overlays += "red"
|
||||
if (locked)
|
||||
overlays += "bolted"
|
||||
return overlays
|
||||
|
||||
overlays += "green"
|
||||
|
||||
return overlays
|
||||
|
||||
/obj/machinery/bci_implanter/attack_hand_secondary(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if (. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
|
||||
return .
|
||||
|
||||
if(!user.Adjacent(src))
|
||||
return
|
||||
|
||||
if (locked)
|
||||
balloon_alert(user, "it's locked!")
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
var/obj/item/organ/cyberimp/bci/bci_to_implant_resolved = bci_to_implant?.resolve()
|
||||
if (isnull(bci_to_implant_resolved))
|
||||
balloon_alert(user, "no bci inserted!")
|
||||
else
|
||||
user.put_in_hands(bci_to_implant_resolved)
|
||||
balloon_alert(user, "ejected bci")
|
||||
|
||||
bci_to_implant = null
|
||||
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/obj/machinery/bci_implanter/attackby(obj/item/weapon, mob/user, params)
|
||||
var/obj/item/organ/cyberimp/bci/new_bci = weapon
|
||||
if (istype(new_bci))
|
||||
if (!(locate(/obj/item/integrated_circuit) in new_bci))
|
||||
balloon_alert(user, "bci has no circuit!")
|
||||
return
|
||||
|
||||
var/obj/item/organ/cyberimp/bci/previous_bci_to_implant = bci_to_implant?.resolve()
|
||||
|
||||
bci_to_implant = WEAKREF(weapon)
|
||||
weapon.forceMove(src)
|
||||
|
||||
if (isnull(previous_bci_to_implant))
|
||||
balloon_alert(user, "inserted bci")
|
||||
else
|
||||
balloon_alert(user, "swapped bci")
|
||||
user.put_in_hands(previous_bci_to_implant)
|
||||
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/machinery/bci_implanter/attackby_secondary(obj/item/weapon, mob/user, params)
|
||||
if (!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, weapon))
|
||||
update_appearance()
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
if (default_pry_open(weapon))
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
if (default_deconstruction_crowbar(weapon))
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/obj/machinery/bci_implanter/proc/start_process()
|
||||
if (machine_stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
if ((machine_stat & MAINT) || panel_open)
|
||||
return
|
||||
if (!occupant || busy)
|
||||
return
|
||||
|
||||
var/locked_state = locked
|
||||
locked = TRUE
|
||||
|
||||
set_busy(TRUE, "[initial(icon_state)]_raising")
|
||||
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_active"), 1 SECONDS)
|
||||
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_falling"), 2 SECONDS)
|
||||
addtimer(CALLBACK(src, .proc/complete_process, locked_state), 3 SECONDS)
|
||||
|
||||
/obj/machinery/bci_implanter/proc/complete_process(locked_state)
|
||||
locked = locked_state
|
||||
set_busy(FALSE)
|
||||
|
||||
var/mob/living/carbon/carbon_occupant = occupant
|
||||
if (!istype(carbon_occupant))
|
||||
return
|
||||
|
||||
playsound(loc, 'sound/machines/ping.ogg', 30, FALSE)
|
||||
|
||||
var/obj/item/organ/cyberimp/bci/bci_organ = carbon_occupant.getorgan(/obj/item/organ/cyberimp/bci)
|
||||
var/obj/item/organ/cyberimp/bci/bci_to_implant_resolved = bci_to_implant?.resolve()
|
||||
|
||||
if (bci_organ)
|
||||
bci_organ.Remove(carbon_occupant)
|
||||
|
||||
if (isnull(bci_to_implant_resolved))
|
||||
say("Occupant's previous brain-computer interface has been transferred to internal storage unit.")
|
||||
bci_organ.forceMove(src)
|
||||
bci_to_implant = WEAKREF(bci_organ)
|
||||
else
|
||||
say("Occupant's previous brain-computer interface has been ejected.")
|
||||
bci_organ.forceMove(drop_location())
|
||||
else if (!isnull(bci_to_implant_resolved))
|
||||
say("Occupant has been injected with [bci_to_implant_resolved].")
|
||||
bci_to_implant_resolved.Insert(carbon_occupant)
|
||||
bci_to_implant = null
|
||||
|
||||
/obj/machinery/bci_implanter/open_machine()
|
||||
if(state_open)
|
||||
return FALSE
|
||||
|
||||
..()
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/bci_implanter/close_machine(mob/living/carbon/user)
|
||||
if(!state_open)
|
||||
return FALSE
|
||||
|
||||
..()
|
||||
|
||||
var/mob/living/carbon/carbon_occupant = occupant
|
||||
if (istype(occupant))
|
||||
var/obj/item/organ/cyberimp/bci/existing_bci_organ = carbon_occupant.getorgan(/obj/item/organ/cyberimp/bci)
|
||||
if (isnull(existing_bci_organ) && isnull(bci_to_implant?.resolve()))
|
||||
say("No brain-computer interface inserted, and occupant does not have one. Insert a BCI to implant one.")
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
|
||||
return FALSE
|
||||
|
||||
addtimer(CALLBACK(src, .proc/start_process), 1 SECONDS)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/bci_implanter/relaymove(mob/living/user, direction)
|
||||
var/message
|
||||
|
||||
if (locked)
|
||||
message = "it won't budge!"
|
||||
else if (user.stat != CONSCIOUS)
|
||||
message = "you don't have the energy!"
|
||||
|
||||
if (!isnull(message))
|
||||
if (COOLDOWN_FINISHED(src, message_cooldown))
|
||||
COOLDOWN_START(src, message_cooldown, 5 SECONDS)
|
||||
balloon_alert(user, message)
|
||||
|
||||
return
|
||||
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/bci_implanter/interact(mob/user)
|
||||
if (state_open)
|
||||
close_machine(null, user)
|
||||
return
|
||||
else if (locked)
|
||||
balloon_alert(user, "it's locked!")
|
||||
return
|
||||
|
||||
open_machine()
|
||||
|
||||
/obj/item/circuitboard/machine/bci_implanter
|
||||
name = "Brain-Computer Interface Manipulation Chamber (Machine Board)"
|
||||
greyscale_colors = CIRCUIT_COLOR_SCIENCE
|
||||
build_path = /obj/machinery/bci_implanter
|
||||
req_components = list(
|
||||
/obj/item/stock_parts/micro_laser = 2,
|
||||
/obj/item/stock_parts/manipulator = 1,
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* # Compact Remote
|
||||
*
|
||||
* A handheld device with one big button.
|
||||
*/
|
||||
/obj/item/compact_remote
|
||||
name = "compact remote"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_small_simple"
|
||||
inhand_icon_state = "electronic"
|
||||
worn_icon_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
light_system = MOVABLE_LIGHT_DIRECTIONAL
|
||||
light_on = FALSE
|
||||
|
||||
/obj/item/compact_remote/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/compact_remote()
|
||||
), SHELL_CAPACITY_SMALL)
|
||||
|
||||
/obj/item/circuit_component/compact_remote
|
||||
display_name = "Compact Remote"
|
||||
desc = "Used to receive inputs from the compact remote shell. Use the shell in hand to trigger the output signal."
|
||||
|
||||
/// Called when attack_self is called on the shell.
|
||||
var/datum/port/output/signal
|
||||
/// The user who used the bot
|
||||
var/datum/port/output/entity
|
||||
|
||||
/obj/item/circuit_component/compact_remote/Initialize()
|
||||
. = ..()
|
||||
entity = add_output_port("User", PORT_TYPE_ATOM)
|
||||
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/compact_remote/register_shell(atom/movable/shell)
|
||||
RegisterSignal(shell, COMSIG_ITEM_ATTACK_SELF, .proc/send_trigger)
|
||||
|
||||
/obj/item/circuit_component/compact_remote/unregister_shell(atom/movable/shell)
|
||||
UnregisterSignal(shell, COMSIG_ITEM_ATTACK_SELF)
|
||||
|
||||
/**
|
||||
* Called when the shell item is used in hand.
|
||||
*/
|
||||
/obj/item/circuit_component/compact_remote/proc/send_trigger(atom/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
source.balloon_alert(user, "clicked primary button")
|
||||
playsound(source, get_sfx("terminal_type"), 25, FALSE)
|
||||
entity.set_output(user)
|
||||
signal.set_output(COMPONENT_SIGNAL)
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* # Compact Remote
|
||||
*
|
||||
* A handheld device with several buttons.
|
||||
* In game, this translates to having different signals for normal usage, alt-clicking, and ctrl-clicking when in your hand.
|
||||
*/
|
||||
/obj/item/controller
|
||||
name = "controller"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_small_calc"
|
||||
inhand_icon_state = "electronic"
|
||||
worn_icon_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
light_system = MOVABLE_LIGHT_DIRECTIONAL
|
||||
light_on = FALSE
|
||||
|
||||
/obj/item/controller/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/controller()
|
||||
), SHELL_CAPACITY_MEDIUM)
|
||||
|
||||
/obj/item/circuit_component/controller
|
||||
display_name = "Controller"
|
||||
desc = "Used to receive inputs from the controller shell. Use the shell in hand to trigger the output signal. Alt-click for the alternate signal. Right click for the extra signal."
|
||||
|
||||
/// The three separate buttons that are called in attack_hand on the shell.
|
||||
var/datum/port/output/signal
|
||||
var/datum/port/output/alt
|
||||
var/datum/port/output/right
|
||||
|
||||
/// The entity output
|
||||
var/datum/port/output/entity
|
||||
|
||||
/obj/item/circuit_component/controller/Initialize()
|
||||
. = ..()
|
||||
entity = add_output_port("User", PORT_TYPE_ATOM)
|
||||
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
|
||||
alt = add_output_port("Alternate Signal", PORT_TYPE_SIGNAL)
|
||||
right = add_output_port("Extra Signal", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/controller/register_shell(atom/movable/shell)
|
||||
RegisterSignal(shell, COMSIG_ITEM_ATTACK_SELF, .proc/send_trigger)
|
||||
RegisterSignal(shell, COMSIG_CLICK_ALT, .proc/send_alternate_signal)
|
||||
RegisterSignal(shell, COMSIG_ITEM_ATTACK_SELF_SECONDARY, .proc/send_right_signal)
|
||||
|
||||
/obj/item/circuit_component/controller/unregister_shell(atom/movable/shell)
|
||||
UnregisterSignal(shell, list(
|
||||
COMSIG_ITEM_ATTACK_SELF,
|
||||
COMSIG_ITEM_ATTACK_SELF_SECONDARY,
|
||||
COMSIG_CLICK_ALT,
|
||||
))
|
||||
|
||||
/**
|
||||
* Called when the shell item is used in hand
|
||||
*/
|
||||
/obj/item/circuit_component/controller/proc/send_trigger(atom/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
if(!user.Adjacent(source))
|
||||
return
|
||||
source.balloon_alert(user, "clicked primary button")
|
||||
playsound(source, get_sfx("terminal_type"), 25, FALSE)
|
||||
entity.set_output(user)
|
||||
signal.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
/**
|
||||
* Called when the shell item is alt-clicked
|
||||
*/
|
||||
/obj/item/circuit_component/controller/proc/send_alternate_signal(atom/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
if(!user.Adjacent(source))
|
||||
return
|
||||
source.balloon_alert(user, "clicked alternate button")
|
||||
playsound(source, get_sfx("terminal_type"), 25, FALSE)
|
||||
entity.set_output(user)
|
||||
alt.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
/**
|
||||
* Called when the shell item is right-clicked in active hand
|
||||
*/
|
||||
/obj/item/circuit_component/controller/proc/send_right_signal(atom/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
if(!user.Adjacent(source))
|
||||
return
|
||||
source.balloon_alert(user, "clicked extra button")
|
||||
playsound(source, get_sfx("terminal_type"), 25, FALSE)
|
||||
entity.set_output(user)
|
||||
right.set_output(COMPONENT_SIGNAL)
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* # Drone
|
||||
*
|
||||
* A movable mob that can be fed inputs on which direction to travel.
|
||||
*/
|
||||
/mob/living/circuit_drone
|
||||
name = "drone"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_medium_med"
|
||||
living_flags = 0
|
||||
light_system = MOVABLE_LIGHT_DIRECTIONAL
|
||||
light_on = FALSE
|
||||
|
||||
/mob/living/circuit_drone/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/bot_circuit()
|
||||
), SHELL_CAPACITY_LARGE)
|
||||
|
||||
/mob/living/circuit_drone/updatehealth()
|
||||
. = ..()
|
||||
if(health < 0)
|
||||
gib(no_brain = TRUE, no_organs = TRUE, no_bodyparts = TRUE)
|
||||
|
||||
/mob/living/circuit_drone/spawn_gibs()
|
||||
new /obj/effect/gibspawner/robot(drop_location(), src, get_static_viruses())
|
||||
|
||||
/obj/item/circuit_component/bot_circuit
|
||||
display_name = "Drone"
|
||||
desc = "Used to send movement output signals to the drone shell."
|
||||
|
||||
/// The inputs to allow for the drone to move
|
||||
var/datum/port/input/north
|
||||
var/datum/port/input/east
|
||||
var/datum/port/input/south
|
||||
var/datum/port/input/west
|
||||
|
||||
// Done like this so that travelling diagonally is more simple
|
||||
COOLDOWN_DECLARE(north_delay)
|
||||
COOLDOWN_DECLARE(east_delay)
|
||||
COOLDOWN_DECLARE(south_delay)
|
||||
COOLDOWN_DECLARE(west_delay)
|
||||
|
||||
/// Delay between each movement
|
||||
var/move_delay = 0.2 SECONDS
|
||||
|
||||
/obj/item/circuit_component/bot_circuit/Initialize()
|
||||
. = ..()
|
||||
north = add_input_port("Move North", PORT_TYPE_SIGNAL)
|
||||
east = add_input_port("Move East", PORT_TYPE_SIGNAL)
|
||||
south = add_input_port("Move South", PORT_TYPE_SIGNAL)
|
||||
west = add_input_port("Move West", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/bot_circuit/input_received(datum/port/input/port)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
var/mob/living/shell = parent.shell
|
||||
if(!istype(shell) || shell.stat)
|
||||
return
|
||||
|
||||
var/direction
|
||||
|
||||
if(COMPONENT_TRIGGERED_BY(north, port) && COOLDOWN_FINISHED(src, north_delay))
|
||||
direction = NORTH
|
||||
COOLDOWN_START(src, north_delay, move_delay)
|
||||
else if(COMPONENT_TRIGGERED_BY(east, port) && COOLDOWN_FINISHED(src, east_delay))
|
||||
direction = EAST
|
||||
COOLDOWN_START(src, east_delay, move_delay)
|
||||
else if(COMPONENT_TRIGGERED_BY(south, port) && COOLDOWN_FINISHED(src, south_delay))
|
||||
direction = SOUTH
|
||||
COOLDOWN_START(src, south_delay, move_delay)
|
||||
else if(COMPONENT_TRIGGERED_BY(west, port) && COOLDOWN_FINISHED(src, west_delay))
|
||||
direction = WEST
|
||||
COOLDOWN_START(src, west_delay, move_delay)
|
||||
|
||||
if(!direction)
|
||||
return
|
||||
|
||||
if(shell.Process_Spacemove(direction))
|
||||
shell.Move(get_step(shell, direction), direction)
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* # Money Bot
|
||||
*
|
||||
* Immobile (but not dense) shell that can receive and dispense money.
|
||||
*/
|
||||
/obj/structure/money_bot
|
||||
name = "money bot"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_large"
|
||||
|
||||
density = FALSE
|
||||
light_system = MOVABLE_LIGHT
|
||||
light_on = FALSE
|
||||
|
||||
var/stored_money = 0
|
||||
|
||||
/obj/structure/money_bot/deconstruct(disassembled)
|
||||
new /obj/item/holochip(drop_location(), stored_money)
|
||||
return ..()
|
||||
|
||||
/obj/structure/money_bot/proc/add_money(to_add)
|
||||
stored_money += to_add
|
||||
SEND_SIGNAL(src, COMSIG_MONEYBOT_ADD_MONEY, to_add)
|
||||
|
||||
/obj/structure/money_bot/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/money_bot(),
|
||||
new /obj/item/circuit_component/money_dispenser()
|
||||
), SHELL_CAPACITY_LARGE)
|
||||
|
||||
/obj/structure/money_bot/wrench_act(mob/living/user, obj/item/tool)
|
||||
set_anchored(!anchored)
|
||||
tool.play_tool_sound(src)
|
||||
balloon_alert(user, "You [anchored?"secure":"unsecure"] [src].")
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/circuit_component/money_dispenser
|
||||
display_name = "Money Dispenser"
|
||||
desc = "Used to dispense money from the money bot. Money is taken from the internal storage of money."
|
||||
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
|
||||
|
||||
/// The amount of money to dispense
|
||||
var/datum/port/input/dispense_amount
|
||||
|
||||
/// Outputs a signal when it fails to output any money.
|
||||
var/datum/port/output/on_fail
|
||||
|
||||
var/obj/structure/money_bot/attached_bot
|
||||
|
||||
/obj/item/circuit_component/money_dispenser/Initialize()
|
||||
. = ..()
|
||||
dispense_amount = add_input_port("Amount", PORT_TYPE_NUMBER)
|
||||
on_fail = add_output_port("On Failed", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/money_dispenser/register_shell(atom/movable/shell)
|
||||
. = ..()
|
||||
if(istype(shell, /obj/structure/money_bot))
|
||||
attached_bot = shell
|
||||
|
||||
/obj/item/circuit_component/money_dispenser/unregister_shell(atom/movable/shell)
|
||||
attached_bot = null
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/money_dispenser/input_received(datum/port/input/port)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
if(!attached_bot)
|
||||
return
|
||||
|
||||
var/to_dispense = clamp(dispense_amount.value, 0, attached_bot.stored_money)
|
||||
if(!to_dispense)
|
||||
on_fail.set_output(COMPONENT_SIGNAL)
|
||||
return
|
||||
|
||||
attached_bot.add_money(-to_dispense)
|
||||
new /obj/item/holochip(drop_location(), to_dispense)
|
||||
|
||||
/obj/item/circuit_component/money_bot
|
||||
display_name = "Money Bot"
|
||||
var/obj/structure/money_bot/attached_bot
|
||||
desc = "Used to receive input signals when money is inserted into the money bot shell and also keep track of the total money in the shell."
|
||||
|
||||
/// Total money in the shell
|
||||
var/datum/port/output/total_money
|
||||
/// Amount of the last money inputted into the shell
|
||||
var/datum/port/output/money_input
|
||||
/// Trigger for when money is inputted into the shell
|
||||
var/datum/port/output/money_trigger
|
||||
/// The person who input the money
|
||||
var/datum/port/output/entity
|
||||
|
||||
/obj/item/circuit_component/money_bot/Initialize()
|
||||
. = ..()
|
||||
total_money = add_output_port("Total Money", PORT_TYPE_NUMBER)
|
||||
money_input = add_output_port("Last Input Money", PORT_TYPE_NUMBER)
|
||||
entity = add_output_port("User", PORT_TYPE_ATOM)
|
||||
money_trigger = add_output_port("Money Input", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/money_bot/register_shell(atom/movable/shell)
|
||||
. = ..()
|
||||
if(istype(shell, /obj/structure/money_bot))
|
||||
attached_bot = shell
|
||||
total_money.set_output(attached_bot.stored_money)
|
||||
RegisterSignal(shell, COMSIG_PARENT_ATTACKBY, .proc/handle_money_insert)
|
||||
RegisterSignal(shell, COMSIG_MONEYBOT_ADD_MONEY, .proc/handle_money_update)
|
||||
|
||||
/obj/item/circuit_component/money_bot/unregister_shell(atom/movable/shell)
|
||||
UnregisterSignal(shell, list(
|
||||
COMSIG_PARENT_ATTACKBY,
|
||||
COMSIG_MONEYBOT_ADD_MONEY,
|
||||
))
|
||||
total_money.set_output(null)
|
||||
attached_bot = null
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/money_bot/proc/handle_money_insert(atom/source, obj/item/item, mob/living/attacker)
|
||||
SIGNAL_HANDLER
|
||||
if(!attached_bot || !iscash(item))
|
||||
return
|
||||
|
||||
var/amount_to_insert = item.get_item_credit_value()
|
||||
if(!amount_to_insert)
|
||||
balloon_alert(attacker, "this has no value!")
|
||||
return
|
||||
|
||||
attached_bot.add_money(amount_to_insert)
|
||||
balloon_alert(attacker, "inserted [amount_to_insert] credits.")
|
||||
money_input.set_output(amount_to_insert)
|
||||
entity.set_output(attacker)
|
||||
money_trigger.set_output(COMPONENT_SIGNAL)
|
||||
qdel(item)
|
||||
|
||||
/obj/item/circuit_component/money_bot/proc/handle_money_update(atom/source)
|
||||
SIGNAL_HANDLER
|
||||
if(attached_bot)
|
||||
total_money.set_output(attached_bot.stored_money)
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* # Scanner
|
||||
*
|
||||
* A handheld device that lets you flash it over people.
|
||||
*/
|
||||
/obj/item/wiremod_scanner
|
||||
name = "scanner"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_small"
|
||||
inhand_icon_state = "electronic"
|
||||
worn_icon_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
light_system = MOVABLE_LIGHT_DIRECTIONAL
|
||||
light_on = FALSE
|
||||
|
||||
/obj/item/wiremod_scanner/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/wiremod_scanner()
|
||||
), SHELL_CAPACITY_SMALL)
|
||||
|
||||
/obj/item/circuit_component/wiremod_scanner
|
||||
display_name = "Scanner"
|
||||
desc = "Used to receive scanned entities from the scanner."
|
||||
|
||||
/// Called when afterattack is called on the shell.
|
||||
var/datum/port/output/signal
|
||||
|
||||
/// The attacker
|
||||
var/datum/port/output/attacker
|
||||
|
||||
/// The entity being attacked
|
||||
var/datum/port/output/attacking
|
||||
|
||||
|
||||
|
||||
/obj/item/circuit_component/wiremod_scanner/Initialize()
|
||||
. = ..()
|
||||
attacker = add_output_port("Scanner", PORT_TYPE_ATOM)
|
||||
attacking = add_output_port("Scanned Entity", PORT_TYPE_ATOM)
|
||||
signal = add_output_port("Scanned", PORT_TYPE_SIGNAL)
|
||||
|
||||
/obj/item/circuit_component/wiremod_scanner/register_shell(atom/movable/shell)
|
||||
RegisterSignal(shell, COMSIG_ITEM_AFTERATTACK, .proc/handle_afterattack)
|
||||
|
||||
/obj/item/circuit_component/wiremod_scanner/unregister_shell(atom/movable/shell)
|
||||
UnregisterSignal(shell, COMSIG_ITEM_AFTERATTACK)
|
||||
|
||||
/**
|
||||
* Called when the shell item attacks something
|
||||
*/
|
||||
/obj/item/circuit_component/wiremod_scanner/proc/handle_afterattack(atom/source, atom/target, mob/user, proximity_flag)
|
||||
SIGNAL_HANDLER
|
||||
if(!proximity_flag)
|
||||
return
|
||||
source.balloon_alert(user, "scanned object")
|
||||
playsound(source, get_sfx("terminal_type"), 25, FALSE)
|
||||
attacker.set_output(user)
|
||||
attacking.set_output(target)
|
||||
signal.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/obj/structure/scanner_gate_shell
|
||||
name = "circuit scanner gate"
|
||||
desc = "A gate able to perform mid-depth scans on any organisms who pass under it."
|
||||
icon = 'icons/obj/machines/scangate.dmi'
|
||||
icon_state = "scangate_black"
|
||||
var/scanline_timer
|
||||
|
||||
/obj/structure/scanner_gate_shell/Initialize()
|
||||
. = ..()
|
||||
set_scanline("passive")
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = .proc/on_entered,
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
AddComponent(/datum/component/shell, list(
|
||||
new /obj/item/circuit_component/scanner_gate()
|
||||
), SHELL_CAPACITY_LARGE, SHELL_FLAG_REQUIRE_ANCHOR)
|
||||
|
||||
/obj/structure/scanner_gate_shell/wrench_act(mob/living/user, obj/item/tool)
|
||||
set_anchored(!anchored)
|
||||
tool.play_tool_sound(src)
|
||||
balloon_alert(user, "You [anchored?"secure":"unsecure"] [src].")
|
||||
return TRUE
|
||||
|
||||
/obj/structure/scanner_gate_shell/proc/on_entered(datum/source, atom/movable/AM)
|
||||
SIGNAL_HANDLER
|
||||
set_scanline("scanning", 10)
|
||||
SEND_SIGNAL(src, COMSIG_SCANGATE_SHELL_PASS, AM)
|
||||
|
||||
/obj/structure/scanner_gate_shell/proc/set_scanline(type, duration)
|
||||
cut_overlays()
|
||||
deltimer(scanline_timer)
|
||||
add_overlay(type)
|
||||
if(duration)
|
||||
scanline_timer = addtimer(CALLBACK(src, .proc/set_scanline, "passive"), duration, TIMER_STOPPABLE)
|
||||
|
||||
/obj/item/circuit_component/scanner_gate
|
||||
display_name = "Scanner Gate"
|
||||
desc = "A gate able to perform mid-depth scans on any object that pass through it."
|
||||
|
||||
circuit_flags = CIRCUIT_FLAG_OUTPUT_SIGNAL
|
||||
|
||||
var/datum/port/output/scanned
|
||||
|
||||
var/obj/structure/scanner_gate_shell/attached_gate
|
||||
|
||||
/obj/item/circuit_component/scanner_gate/Initialize()
|
||||
. = ..()
|
||||
scanned = add_output_port("Scanned Object", PORT_TYPE_ATOM)
|
||||
|
||||
/obj/item/circuit_component/scanner_gate/register_shell(atom/movable/shell)
|
||||
. = ..()
|
||||
if(istype(shell, /obj/structure/scanner_gate_shell))
|
||||
attached_gate = shell
|
||||
RegisterSignal(attached_gate, COMSIG_SCANGATE_SHELL_PASS, .proc/on_trigger)
|
||||
|
||||
/obj/item/circuit_component/scanner_gate/unregister_shell(atom/movable/shell)
|
||||
UnregisterSignal(attached_gate, COMSIG_SCANGATE_SHELL_PASS)
|
||||
attached_gate = null
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/scanner_gate/proc/on_trigger(datum/source, atom/movable/passed)
|
||||
SIGNAL_HANDLER
|
||||
scanned.set_output(passed)
|
||||
trigger_output.set_output(COMPONENT_SIGNAL)
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* # Server
|
||||
*
|
||||
* Immobile (but not dense) shells that can interact with
|
||||
* world.
|
||||
*/
|
||||
/obj/structure/server
|
||||
name = "server"
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "setup_stationary"
|
||||
|
||||
density = TRUE
|
||||
light_system = MOVABLE_LIGHT
|
||||
light_on = FALSE
|
||||
|
||||
/obj/structure/server/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/shell, null, SHELL_CAPACITY_VERY_LARGE, SHELL_FLAG_REQUIRE_ANCHOR|SHELL_FLAG_USB_PORT)
|
||||
|
||||
/obj/structure/server/wrench_act(mob/living/user, obj/item/tool)
|
||||
set_anchored(!anchored)
|
||||
tool.play_tool_sound(src)
|
||||
balloon_alert(user, "You [anchored?"secure":"unsecure"] [src].")
|
||||
return TRUE
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* # Shell Item
|
||||
*
|
||||
* Printed out by protolathes. Screwdriver to complete the shell.
|
||||
*/
|
||||
/obj/item/shell
|
||||
name = "assembly"
|
||||
desc = "A shell assembly that can be completed by screwdrivering it."
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
var/shell_to_spawn
|
||||
var/screw_delay = 3 SECONDS
|
||||
|
||||
/obj/item/shell/screwdriver_act(mob/living/user, obj/item/tool)
|
||||
user.visible_message(span_notice("[user] begins finishing [src]."), span_notice("You begin finishing [src]."))
|
||||
tool.play_tool_sound(src)
|
||||
if(!do_after(user, screw_delay, src))
|
||||
return
|
||||
user.visible_message(span_notice("[user] finishes [src]."), span_notice("You finish [src]."))
|
||||
|
||||
var/turf/drop_loc = drop_location()
|
||||
|
||||
qdel(src)
|
||||
if(drop_loc)
|
||||
new shell_to_spawn(drop_loc)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/shell/bot
|
||||
name = "bot assembly"
|
||||
icon_state = "setup_medium_box-open"
|
||||
shell_to_spawn = /obj/structure/bot
|
||||
|
||||
/obj/item/shell/money_bot
|
||||
name = "money bot assembly"
|
||||
icon_state = "setup_large-open"
|
||||
shell_to_spawn = /obj/structure/money_bot
|
||||
|
||||
/obj/item/shell/drone
|
||||
name = "drone assembly"
|
||||
icon_state = "setup_medium_med-open"
|
||||
shell_to_spawn = /mob/living/circuit_drone
|
||||
|
||||
/obj/item/shell/server
|
||||
name = "server assembly"
|
||||
icon_state = "setup_stationary-open"
|
||||
shell_to_spawn = /obj/structure/server
|
||||
screw_delay = 10 SECONDS
|
||||
|
||||
/obj/item/shell/airlock
|
||||
name = "circuit airlock assembly"
|
||||
icon = 'icons/obj/doors/airlocks/station/public.dmi'
|
||||
icon_state = "construction"
|
||||
shell_to_spawn = /obj/machinery/door/airlock/shell
|
||||
screw_delay = 10 SECONDS
|
||||
|
||||
/obj/item/shell/bci
|
||||
name = "brain-computer interface assembly"
|
||||
icon_state = "bci-open"
|
||||
shell_to_spawn = /obj/item/organ/cyberimp/bci
|
||||
|
||||
/obj/item/shell/scanner_gate
|
||||
name = "scanner gate assembly"
|
||||
icon = 'icons/obj/machines/scangate.dmi'
|
||||
icon_state = "scangate_black_open"
|
||||
shell_to_spawn = /obj/structure/scanner_gate_shell
|
||||
Reference in New Issue
Block a user