Files
Bubberstation/code/modules/wiremod/core/integrated_circuit.dm
leaKsi 05c77a3f70 A bunch of sanitization fixes (#89477)
## About The Pull Request

- Adds sanitization to windoor names and circuit shells.
- Fixes a (mostly useless) href exploit with wizard's contracts.

## Why It's Good For The Game
Sanitizing input is probably good.

## Changelog
🆑
fix: Fixed a few sanitization issues.
/🆑
2025-03-12 16:01:51 -04:00

774 lines
26 KiB
Plaintext

/// 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/devices/circuitry_n_data.dmi'
icon_state = "integrated_circuit"
inhand_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
/// 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/power_store/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()
/// Variables stored on this integrated circuit that can be set by a setter, with a `variable_name = value` structure
var/list/datum/circuit_variable/modifiable_circuit_variables = list()
/// List variables stored on this integrated circuit, with a `variable_name = value` structure
var/list/datum/circuit_variable/list_variables = list()
/// Assoc list variables stored on this integrated circuit, with a `variable_name = value` structure
var/list/datum/circuit_variable/assoc_list_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
/// The grid mode state for the circuit.
var/grid_mode = TRUE
/// The current size of the circuit.
var/current_size = 0
/// The current linked component printer. Lets you remotely print off circuit components and places them in the integrated circuit.
var/datum/weakref/linked_component_printer
/obj/item/integrated_circuit/Initialize(mapload)
. = ..()
GLOB.integrated_circuits += src
RegisterSignal(src, COMSIG_ATOM_USB_CABLE_TRY_ATTACH, PROC_REF(on_atom_usb_cable_try_attach))
/obj/item/integrated_circuit/loaded/Initialize(mapload)
. = ..()
set_cell(new /obj/item/stock_parts/power_store/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_ASSOC_VAL(circuit_variables)
QDEL_LIST_ASSOC_VAL(list_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/drop_location()
if(shell)
return shell.drop_location()
return ..()
/**
* Sets the cell of the integrated circuit.
*
* Arguments:
* * cell_to_set - The new cell of the circuit. Can be null.
**/
/obj/item/integrated_circuit/proc/set_cell(obj/item/stock_parts/power_store/cell_to_set)
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_CELL, cell_to_set)
cell = cell_to_set
/**
* Sets the locked status of the integrated circuit.
*
* Arguments:
* * new_value - A boolean that determines if the circuit is locked or not.
**/
/obj/item/integrated_circuit/proc/set_locked(new_value)
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_LOCKED, new_value)
locked = new_value
/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/power_store/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(isidcard(I))
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_QDELETING, PROC_REF(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
attached_component.trigger_component()
/**
* 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_QDELETING)
shell = null
set_on(FALSE)
SEND_SIGNAL(src, COMSIG_CIRCUIT_SHELL_REMOVED)
/**
* Sets the on status of the integrated circuit.
*
* Arguments:
* * new_value - A boolean that determines if the circuit is on or not.
**/
/obj/item/integrated_circuit/proc/set_on(new_value)
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_ON, new_value)
on = new_value
/**
* Used for checking if another component of to_check's type exists in the circuit.
* Introspects through modules.
*
* Arguments:
* * to_check - The component to check.
**/
/obj/item/integrated_circuit/proc/is_duplicate(obj/item/circuit_component/to_check)
for(var/component as anything in attached_components)
if(component == to_check)
continue
if(istype(component, to_check.type))
return TRUE
if(istype(component, /obj/item/circuit_component/module))
var/obj/item/circuit_component/module/module = component
for(var/module_component as anything in module.internal_circuit.attached_components)
if(module_component == to_check)
continue
if(istype(module_component, to_check.type))
return TRUE
return FALSE
/**
* 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 FALSE
if(SEND_SIGNAL(src, COMSIG_CIRCUIT_ADD_COMPONENT, to_add, user) & COMPONENT_CANCEL_ADD_COMPONENT)
return FALSE
if(!to_add.add_to(src))
return FALSE
if(to_add.circuit_flags & CIRCUIT_NO_DUPLICATES)
if(is_duplicate(to_add))
to_chat(user, span_danger("You can't insert multiple instances of this component into the same circuit!"))
return FALSE
// SKYRAT ADDITION BEGIN - This is required since we have two reagent components, and the BCI only allows one active
if(to_add.circuit_flags & CIRCUIT_REAGENT_CONTAINER_TYPE)
if(shell && shell.reagents)
to_chat(user, span_danger("You can't insert multiple reagent containers into the same circuit!"))
return FALSE
// SKYRAT ADDITION END
var/success = FALSE
if(user)
success = user.transferItemToLoc(to_add, src)
else
success = to_add.forceMove(src)
if(!success)
return FALSE
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
SEND_SIGNAL(to_add, COMSIG_CIRCUIT_COMPONENT_ADDED, src)
to_add.parent = src
attached_components += to_add
current_size += to_add.circuit_size
RegisterSignal(to_add, COMSIG_MOVABLE_MOVED, PROC_REF(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)
current_size -= to_remove.circuit_size
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),
get_asset_datum(/datum/asset/json/circuit_components)
)
/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
var/obj/machinery/component_printer/printer = linked_component_printer?.resolve()
if(!printer)
return
.["stored_designs"] = printer.current_unlocked_designs
/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,
"datatype_data" = port.datatype_ui_data(user)
))
component_data["name"] = component.display_name
component_data["x"] = component.rel_x
component_data["y"] = component.rel_y
component_data["removable"] = component.removable
component_data["color"] = component.ui_color
component_data["category"] = component.category
component_data["ui_alerts"] = component.ui_alerts
component_data["ui_buttons"] = component.ui_buttons
.["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
if(islist(variable.value))
variable_data["is_list"] = TRUE
.["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
.["grid_mode"] = grid_mode
.["is_admin"] = (admin_only || isAdminGhostAI(user)) && 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, datum/ui_state/state)
. = ..()
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, datum/tgui/ui)
. = ..()
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)
var/mob/user = ui.user
if(component.loc == src)
user.put_in_hands(component)
var/obj/machinery/component_printer/printer = linked_component_printer?.resolve()
if (!isnull(printer))
printer.base_item_interaction(user, 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_handler.datatype_flags & DATATYPE_FLAG_ALLOW_ATOM_INPUT))
return
var/obj/item/multitool/circuit/marker = usr.get_active_held_item()
// Let's admins upload marked datums to an entity port.
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(params["display_name"])
else
set_display_name("")
. = TRUE
if("toggle_grid_mode")
toggle_grid_mode()
. = TRUE
if("set_examined_component")
var/component_id = text2num(params["component_id"])
if(!WITHIN_RANGE(component_id, attached_components))
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
if(params["is_list"])
variable_datatype = PORT_TYPE_LIST(variable_datatype)
else if(params["is_assoc_list"])
variable_datatype = PORT_TYPE_ASSOC_LIST(PORT_TYPE_STRING, variable_datatype)
var/datum/circuit_variable/variable = new /datum/circuit_variable(variable_identifier, variable_datatype)
if(params["is_assoc_list"])
variable.set_value(list())
assoc_list_variables[variable_identifier] = variable
else if(params["is_list"])
variable.set_value(list())
list_variables[variable_identifier] = variable
else
modifiable_circuit_variables[variable_identifier] = variable
circuit_variables[variable_identifier] = variable
. = 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
list_variables -= variable_identifier
modifiable_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/variable/getter
if(params["is_setter"])
designated_type = /obj/item/circuit_component/variable/setter
var/obj/item/circuit_component/variable/component = new designated_type(src)
if(!add_component(component, usr))
qdel(component)
return
component.variable_name.set_input(params["variable"])
component.rel_x = text2num(params["rel_x"])
component.rel_y = text2num(params["rel_y"])
RegisterSignal(component, COMSIG_CIRCUIT_COMPONENT_REMOVED, PROC_REF(clear_setter_or_getter))
setter_and_getter_count++
return TRUE
if("move_screen")
screen_x = text2num(params["screen_x"])
screen_y = text2num(params["screen_y"])
if("perform_action")
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]
SEND_SIGNAL(component, COMSIG_CIRCUIT_COMPONENT_PERFORM_ACTION, ui.user, params["action_name"])
component.ui_perform_action(ui.user, params["action_name"])
if("print_component")
var/component_path = text2path(params["component_to_print"])
var/obj/item/circuit_component/component
if((!admin_only && !isAdminGhostAI(ui.user)) || !check_rights_for(ui.user.client, R_SPAWN))
var/obj/machinery/component_printer/printer = linked_component_printer?.resolve()
if(!printer)
balloon_alert(ui.user, "linked printer not found!")
return
component = printer.print_component(component_path)
if(!component)
balloon_alert(ui.user, "failed to make the component!")
return
else
if(!ispath(component_path, /obj/item/circuit_component))
return
component = new component_path(drop_location())
component.datum_flags |= DF_VAR_EDITED
if(!add_component(component))
return
component.rel_x = text2num(params["rel_x"])
component.rel_y = text2num(params["rel_y"])
return TRUE
#undef WITHIN_RANGE
/obj/item/integrated_circuit/balloon_alert(mob/viewer, text)
if(shell)
return shell.balloon_alert(viewer, text)
return ..()
/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
/// Sets the display name that appears on the shell.
/obj/item/integrated_circuit/proc/set_display_name(new_name)
display_name = copytext_char(new_name, 1, label_max_length)
if(!shell)
return
if(display_name != "")
if(!admin_only)
shell.name = "[initial(shell.name)] ([strip_html(display_name)])"
else
shell.name = display_name
else
shell.name = initial(shell.name)
/// Toggles the grid mode property for this circuit.
/obj/item/integrated_circuit/proc/toggle_grid_mode()
grid_mode = !grid_mode
/**
* Returns the creator of the integrated circuit. Used in admin messages and other related things.
*/
/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
/obj/item/integrated_circuit/admin
name = "administrative circuit"
desc = "The components installed in here are far beyond your comprehension."
admin_only = TRUE