mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
## 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. /🆑
774 lines
26 KiB
Plaintext
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
|