mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
[MIRROR] Circuitry cloning implementation (#11536)
Co-authored-by: Aura Dusklight <46622484+NovaDusklight@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
bc177617ef
commit
eabb9e4b20
@@ -18,6 +18,7 @@
|
||||
var/locked = FALSE // If true, the assembly cannot be opened with a crowbar
|
||||
var/obj/item/card/id/locked_by = null // The ID that locked this assembly
|
||||
var/obj/item/card/id/access_card = null // ID card for door access
|
||||
var/list/component_positions = list() // Stores circuit positions as list of lists: list("ref" = ref, "x" = x, "y" = y)
|
||||
|
||||
|
||||
/obj/item/electronic_assembly/Initialize(mapload)
|
||||
@@ -86,11 +87,18 @@
|
||||
data["battery_max"] = round(battery?.maxcharge, 0.1)
|
||||
data["net_power"] = net_power / CELLRATE
|
||||
|
||||
// Include export data - the UI component will handle displaying it if needed
|
||||
data["export_data"] = serialize_electronic_assembly()
|
||||
data["assembly_name"] = name
|
||||
|
||||
var/list/circuits = list()
|
||||
for(var/obj/item/integrated_circuit/circuit in contents)
|
||||
UNTYPED_LIST_ADD(circuits, circuit.tgui_data(user, ui, state))
|
||||
data["circuits"] = circuits
|
||||
|
||||
// Include component positions for UI restoration
|
||||
data["component_positions"] = component_positions
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/electronic_assembly/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
@@ -98,6 +106,14 @@
|
||||
return TRUE
|
||||
|
||||
switch(action)
|
||||
if("export_circuit")
|
||||
if(!LAZYLEN(contents))
|
||||
to_chat(ui.user, span_warning("There's nothing in the [src] to export!"))
|
||||
return TRUE
|
||||
var/datum/tgui/window = new(ui.user, src, "ICExport", "Circuit Export")
|
||||
window.open()
|
||||
return TRUE
|
||||
|
||||
// Actual assembly actions
|
||||
if("rename")
|
||||
rename(ui.user)
|
||||
@@ -171,6 +187,30 @@
|
||||
C.remove(ui.user)
|
||||
return TRUE
|
||||
|
||||
if("update_component_position")
|
||||
var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents
|
||||
if(!istype(C))
|
||||
return FALSE
|
||||
|
||||
var/new_x = params["x"]
|
||||
var/new_y = params["y"]
|
||||
if(!isnum(new_x) || !isnum(new_y))
|
||||
return FALSE
|
||||
|
||||
// Find existing position entry or create new one
|
||||
var/found = FALSE
|
||||
for(var/list/pos_data in component_positions)
|
||||
if(pos_data["ref"] == REF(C))
|
||||
pos_data["x"] = new_x
|
||||
pos_data["y"] = new_y
|
||||
found = TRUE
|
||||
break
|
||||
|
||||
if(!found)
|
||||
UNTYPED_LIST_ADD(component_positions, list("ref" = REF(C), "x" = new_x, "y" = new_y))
|
||||
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
// End TGUI
|
||||
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
/**
|
||||
* Serialization/Deserialization for Integrated Circuits
|
||||
*
|
||||
* These functions handle converting assemblies to JSON format for export/import
|
||||
*/
|
||||
// Common prefixes that can be stripped to reduce JSON size
|
||||
#define ASSEMBLY_PREFIX "/obj/item/electronic_assembly/"
|
||||
#define CIRCUIT_PREFIX "/obj/item/integrated_circuit/"
|
||||
|
||||
/**
|
||||
* Strips the common assembly prefix to reduce JSON size
|
||||
* @param type_path The full assembly type path
|
||||
* @return The shortened path without the common prefix
|
||||
*/
|
||||
/obj/item/electronic_assembly/proc/strip_assembly_prefix(type_path)
|
||||
if(!type_path)
|
||||
return ""
|
||||
var/type_string = "[type_path]"
|
||||
if(findtext(type_string, ASSEMBLY_PREFIX) == 1)
|
||||
return copytext(type_string, length(ASSEMBLY_PREFIX) + 1)
|
||||
return type_string // Return as-is if prefix not found (for backward compatibility)
|
||||
|
||||
/**
|
||||
* Strips the common circuit prefix to reduce JSON size
|
||||
* @param type_path The full circuit type path
|
||||
* @return The shortened path without the common prefix
|
||||
*/
|
||||
/obj/item/electronic_assembly/proc/strip_circuit_prefix(type_path)
|
||||
if(!type_path)
|
||||
return ""
|
||||
var/type_string = "[type_path]"
|
||||
if(findtext(type_string, CIRCUIT_PREFIX) == 1)
|
||||
return copytext(type_string, length(CIRCUIT_PREFIX) + 1)
|
||||
return type_string // Return as-is if prefix not found (for backward compatibility)
|
||||
|
||||
/**
|
||||
* Restores the full assembly path from a shortened one
|
||||
* @param shortened_path The shortened assembly path
|
||||
* @return The full assembly type path
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/restore_assembly_prefix(shortened_path)
|
||||
if(!shortened_path)
|
||||
return ""
|
||||
var/path_string = "[shortened_path]"
|
||||
// If it already contains the full path, return as-is (backward compatibility)
|
||||
if(findtext(path_string, "/obj/item/electronic_assembly/") == 1)
|
||||
return path_string
|
||||
// Otherwise, add the prefix
|
||||
return ASSEMBLY_PREFIX + path_string
|
||||
|
||||
/**
|
||||
* Restores the full circuit path from a shortened one
|
||||
* @param shortened_path The shortened circuit path
|
||||
* @return The full circuit type path
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/restore_circuit_prefix(shortened_path)
|
||||
if(!shortened_path)
|
||||
return ""
|
||||
var/path_string = "[shortened_path]"
|
||||
// If it already contains the full path, return as-is (backward compatibility)
|
||||
if(findtext(path_string, "/obj/item/integrated_circuit/") == 1)
|
||||
return path_string
|
||||
// Otherwise, add the prefix
|
||||
return CIRCUIT_PREFIX + path_string
|
||||
|
||||
/**
|
||||
* Serializes this electronic assembly into a JSON string
|
||||
*
|
||||
* @return JSON string representation of the assembly
|
||||
*/
|
||||
/obj/item/electronic_assembly/proc/serialize_electronic_assembly()
|
||||
if(!istype(src))
|
||||
return "Invalid assembly"
|
||||
|
||||
var/list/assembly_data = list(
|
||||
"n" = name, // Shortened: name
|
||||
"d" = desc, // Shortened: desc
|
||||
"t" = src.strip_assembly_prefix("[type]"), // Shortened: type (strip common prefix)
|
||||
"c" = detail_color, // Shortened: color
|
||||
"components" = list(), // Keep full name for clarity
|
||||
"connections" = list() // Keep full name for clarity
|
||||
)
|
||||
|
||||
// Create a lookup table for component indices
|
||||
var/list/component_indices = list()
|
||||
var/component_index = 1
|
||||
|
||||
// First pass: serialize components and build index lookup
|
||||
for(var/obj/item/integrated_circuit/IC in contents)
|
||||
component_indices[REF(IC)] = component_index
|
||||
|
||||
var/list/component_data = list(
|
||||
"i" = component_index, // Shortened key: index
|
||||
"t" = src.strip_circuit_prefix("[IC.type]") // Shortened key: type (strip common prefix)
|
||||
)
|
||||
|
||||
// Only include custom name if it differs from the default
|
||||
if(IC.displayed_name != IC.name)
|
||||
component_data["n"] = IC.displayed_name // Shortened key: name
|
||||
|
||||
// Include position data if available
|
||||
for(var/list/pos_data in component_positions)
|
||||
if(pos_data["ref"] == REF(IC))
|
||||
component_data["x"] = pos_data["x"] // x position
|
||||
component_data["y"] = pos_data["y"] // y position
|
||||
break
|
||||
|
||||
// Serialize pin data that has non-null values (both inputs and outputs)
|
||||
var/list/pin_data_list = list()
|
||||
|
||||
// Serialize input pins
|
||||
for(var/i = 1, i <= length(IC.inputs), i++)
|
||||
var/datum/integrated_io/input_pin = IC.inputs[i]
|
||||
if(input_pin.data != null)
|
||||
var/pin_data = null
|
||||
// Handle different data types appropriately
|
||||
if(isnum(input_pin.data) || istext(input_pin.data))
|
||||
pin_data = input_pin.data
|
||||
else if(islist(input_pin.data))
|
||||
var/list/original_list = input_pin.data
|
||||
pin_data = list()
|
||||
for(var/item in original_list)
|
||||
pin_data += item
|
||||
else
|
||||
pin_data = "[input_pin.data]" // Convert other types to text
|
||||
|
||||
// Store pin type, index and data
|
||||
UNTYPED_LIST_ADD(pin_data_list, list("t" = "i", "i" = i, "d" = pin_data))
|
||||
|
||||
// Serialize output pins
|
||||
for(var/i = 1, i <= length(IC.outputs), i++)
|
||||
var/datum/integrated_io/output_pin = IC.outputs[i]
|
||||
if(output_pin.data != null)
|
||||
var/pin_data = null
|
||||
// Handle different data types appropriately
|
||||
if(isnum(output_pin.data) || istext(output_pin.data))
|
||||
pin_data = output_pin.data
|
||||
else if(islist(output_pin.data))
|
||||
var/list/original_list = output_pin.data
|
||||
pin_data = list()
|
||||
for(var/item in original_list)
|
||||
pin_data += item
|
||||
else
|
||||
pin_data = "[output_pin.data]" // Convert other types to text
|
||||
|
||||
// Store pin type, index and data
|
||||
UNTYPED_LIST_ADD(pin_data_list, list("t" = "o", "i" = i, "d" = pin_data))
|
||||
|
||||
// Only include pins if there's actual data
|
||||
if(length(pin_data_list) > 0)
|
||||
component_data["p"] = pin_data_list // Shortened key: pins (inputs and outputs)
|
||||
|
||||
UNTYPED_LIST_ADD(assembly_data["components"], component_data)
|
||||
component_index++
|
||||
|
||||
// Second pass: serialize connections using the component indices (avoid duplicates by only processing outputs)
|
||||
var/list/recorded_connections = list() // Track connections to avoid duplicates
|
||||
|
||||
for(var/obj/item/integrated_circuit/IC in contents)
|
||||
var/source_component_index = component_indices[REF(IC)]
|
||||
|
||||
// Check output connections (only process outputs to avoid duplicates)
|
||||
for(var/i = 1, i <= IC.outputs.len, i++)
|
||||
var/datum/integrated_io/output_pin = IC.outputs[i]
|
||||
for(var/datum/integrated_io/linked_pin in output_pin.linked)
|
||||
var/target_component_index = component_indices[REF(linked_pin.holder)]
|
||||
if(target_component_index)
|
||||
var/target_pin_type = "u" // u = unknown
|
||||
var/target_pin_index = 0
|
||||
|
||||
if(linked_pin in linked_pin.holder.inputs)
|
||||
target_pin_type = "i" // i = input
|
||||
target_pin_index = linked_pin.holder.inputs.Find(linked_pin)
|
||||
else if(linked_pin in linked_pin.holder.outputs)
|
||||
target_pin_type = "o" // o = output
|
||||
target_pin_index = linked_pin.holder.outputs.Find(linked_pin)
|
||||
else if(linked_pin in linked_pin.holder.activators)
|
||||
target_pin_type = "a" // a = activator
|
||||
target_pin_index = linked_pin.holder.activators.Find(linked_pin)
|
||||
|
||||
if(target_pin_index > 0)
|
||||
// Create unique connection identifier to prevent duplicates
|
||||
var/connection_id = "[source_component_index].o[i]->[target_component_index].[target_pin_type][target_pin_index]"
|
||||
|
||||
if(!(connection_id in recorded_connections))
|
||||
recorded_connections += connection_id
|
||||
|
||||
// Ultra-compact connection format: [sc, spt, spi, tc, tpt, tpi]
|
||||
var/list/connection = list(
|
||||
"sc" = source_component_index, // source_component -> sc
|
||||
"spt" = "o", // source_pin_type -> spt (always "o" for output)
|
||||
"spi" = i, // source_pin_index -> spi
|
||||
"tc" = target_component_index, // target_component -> tc
|
||||
"tpt" = target_pin_type, // target_pin_type -> tpt
|
||||
"tpi" = target_pin_index // target_pin_index -> tpi
|
||||
)
|
||||
UNTYPED_LIST_ADD(assembly_data["connections"], connection)
|
||||
|
||||
// Check activator connections
|
||||
for(var/i = 1, i <= IC.activators.len, i++)
|
||||
var/datum/integrated_io/activate/activator_pin = IC.activators[i]
|
||||
for(var/datum/integrated_io/linked_pin in activator_pin.linked)
|
||||
var/target_component_index = component_indices[REF(linked_pin.holder)]
|
||||
if(target_component_index)
|
||||
var/target_pin_type = "u" // u = unknown
|
||||
var/target_pin_index = 0
|
||||
|
||||
if(linked_pin in linked_pin.holder.inputs)
|
||||
target_pin_type = "i" // i = input
|
||||
target_pin_index = linked_pin.holder.inputs.Find(linked_pin)
|
||||
else if(linked_pin in linked_pin.holder.outputs)
|
||||
target_pin_type = "o" // o = output
|
||||
target_pin_index = linked_pin.holder.outputs.Find(linked_pin)
|
||||
else if(linked_pin in linked_pin.holder.activators)
|
||||
target_pin_type = "a" // a = activator
|
||||
target_pin_index = linked_pin.holder.activators.Find(linked_pin)
|
||||
|
||||
if(target_pin_index > 0)
|
||||
// Create unique connection identifier to prevent duplicates
|
||||
var/connection_id = "[source_component_index].a[i]->[target_component_index].[target_pin_type][target_pin_index]"
|
||||
|
||||
if(!(connection_id in recorded_connections))
|
||||
recorded_connections += connection_id
|
||||
|
||||
// Ultra-compact connection format: [sc, spt, spi, tc, tpt, tpi]
|
||||
var/list/connection = list(
|
||||
"sc" = source_component_index, // source_component -> sc
|
||||
"spt" = "a", // source_pin_type -> spt (always "a" for activator)
|
||||
"spi" = i, // source_pin_index -> spi
|
||||
"tc" = target_component_index, // target_component -> tc
|
||||
"tpt" = target_pin_type, // target_pin_type -> tpt
|
||||
"tpi" = target_pin_index // target_pin_index -> tpi
|
||||
)
|
||||
UNTYPED_LIST_ADD(assembly_data["connections"], connection)
|
||||
|
||||
return json_encode(assembly_data)
|
||||
|
||||
/**
|
||||
* Deserializes a JSON string into a list of components to add to an assembly
|
||||
*
|
||||
* @param json_data The JSON string to deserialize
|
||||
* @return List of information needed to recreate the assembly
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/deserialize_electronic_assembly(json_data)
|
||||
if(!json_data)
|
||||
return null
|
||||
|
||||
// Add safety check for maximum size
|
||||
if(length(json_data) > 50000)
|
||||
return null
|
||||
|
||||
// Safety check for minimum viable JSON
|
||||
if(length(json_data) < 10)
|
||||
return null
|
||||
|
||||
if(copytext(json_data, 1, 2) != "{" || copytext(json_data, length(json_data)) != "}")
|
||||
return null
|
||||
|
||||
var/list/assembly_data
|
||||
try
|
||||
// Use built-in html_decode to handle all HTML entities
|
||||
var/cleaned_json = html_decode(json_data)
|
||||
|
||||
assembly_data = json_decode(cleaned_json)
|
||||
catch
|
||||
return null
|
||||
|
||||
if(!assembly_data || !islist(assembly_data))
|
||||
return null
|
||||
|
||||
// Validate required fields exist
|
||||
if(!assembly_data["components"] || !islist(assembly_data["components"]))
|
||||
return null
|
||||
|
||||
return assembly_data
|
||||
|
||||
/**
|
||||
* Creates an electronic assembly from deserialized data
|
||||
*
|
||||
* @param assembly_data The deserialized assembly data
|
||||
* @param override_type Whether to override the assembly type
|
||||
* @param custom_type Custom assembly type path if overriding
|
||||
* @return The created assembly or null if failed
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/create_assembly_from_data(list/assembly_data, override_type = FALSE, custom_type = null)
|
||||
if(!assembly_data || !islist(assembly_data))
|
||||
return null
|
||||
|
||||
var/obj/item/electronic_assembly/assembly
|
||||
|
||||
// Determine assembly type
|
||||
if(override_type && custom_type)
|
||||
var/custom_path = text2path(custom_type)
|
||||
if(custom_path && ispath(custom_path, /obj/item/electronic_assembly))
|
||||
assembly = new custom_path()
|
||||
|
||||
// Use original assembly type if not overriding (use shortened key "t" for type)
|
||||
if(!assembly && assembly_data["t"])
|
||||
var/restored_path = src.restore_assembly_prefix(assembly_data["t"])
|
||||
var/original_path = text2path(restored_path)
|
||||
if(original_path && ispath(original_path, /obj/item/electronic_assembly))
|
||||
assembly = new original_path()
|
||||
|
||||
// Default to medium assembly
|
||||
if(!assembly)
|
||||
assembly = new /obj/item/electronic_assembly/medium()
|
||||
|
||||
// Set basic properties (use shortened keys)
|
||||
if(assembly_data["n"]) // "n" for name
|
||||
assembly.name = assembly_data["n"]
|
||||
if(assembly_data["d"]) // "d" for desc
|
||||
assembly.desc = assembly_data["d"]
|
||||
if(assembly_data["c"]) // "c" for color
|
||||
assembly.detail_color = assembly_data["c"]
|
||||
|
||||
// Open assembly for component insertion
|
||||
assembly.opened = TRUE
|
||||
|
||||
return assembly
|
||||
|
||||
/**
|
||||
* Adds components to an assembly from deserialized data
|
||||
*
|
||||
* @param assembly The assembly to add components to
|
||||
* @param assembly_data The deserialized assembly data
|
||||
* @param available_components List of component types available for creation
|
||||
* @return List of created components indexed by their original index
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/add_components_to_assembly(obj/item/electronic_assembly/assembly, list/assembly_data, list/available_components)
|
||||
if(!assembly || !assembly_data || !assembly_data["components"])
|
||||
return null
|
||||
|
||||
var/list/created_components = list()
|
||||
var/list/components_list = assembly_data["components"]
|
||||
|
||||
// Create each component
|
||||
var/component_index = 0
|
||||
for(var/component_data in components_list)
|
||||
component_index++
|
||||
|
||||
if(!islist(component_data))
|
||||
continue
|
||||
|
||||
// Use shortened keys: "t" for type, "i" for index
|
||||
if(!component_data["t"] || !component_data["i"])
|
||||
continue
|
||||
|
||||
var/restored_type_path = src.restore_circuit_prefix(component_data["t"])
|
||||
var/component_type_path = text2path(restored_type_path)
|
||||
if(!component_type_path || !ispath(component_type_path, /obj/item/integrated_circuit))
|
||||
continue
|
||||
|
||||
if(available_components && !(component_type_path in available_components))
|
||||
continue
|
||||
|
||||
var/obj/item/integrated_circuit/IC = new component_type_path()
|
||||
|
||||
if(component_data["n"])
|
||||
IC.displayed_name = component_data["n"]
|
||||
|
||||
// Set pin data - use shortened key "p" for pins (both inputs and outputs)
|
||||
if(component_data["p"] && islist(component_data["p"]))
|
||||
for(var/list/pin_data in component_data["p"])
|
||||
// Only support ultra-compact format with pin type indicators
|
||||
var/pin_type = pin_data["t"] // Pin type: "i" = input, "o" = output
|
||||
var/pin_index = pin_data["i"] // Pin index
|
||||
var/pin_value = pin_data["d"] // Pin data
|
||||
|
||||
if(!pin_type || !pin_index || pin_value == null)
|
||||
continue
|
||||
|
||||
var/datum/integrated_io/target_pin = null
|
||||
|
||||
if(pin_type == "i")
|
||||
if(pin_index <= length(IC.inputs))
|
||||
target_pin = IC.inputs[pin_index]
|
||||
else if(pin_type == "o")
|
||||
if(pin_index <= length(IC.outputs))
|
||||
target_pin = IC.outputs[pin_index]
|
||||
else
|
||||
continue
|
||||
|
||||
if(target_pin)
|
||||
target_pin.write_data_to_pin(pin_value)
|
||||
|
||||
// Add component to assembly
|
||||
IC.forceMove(assembly)
|
||||
assembly.force_add_circuit(IC)
|
||||
|
||||
// Store position data if available
|
||||
if(component_data["x"] != null && component_data["y"] != null)
|
||||
// Store position in assembly for later UI restoration
|
||||
UNTYPED_LIST_ADD(assembly.component_positions, list(
|
||||
"ref" = REF(IC),
|
||||
"x" = component_data["x"],
|
||||
"y" = component_data["y"]
|
||||
))
|
||||
else
|
||||
// Set default positions in a grid layout if no position data
|
||||
var/default_x = ((component_index - 1) % 4) * 200 + 50 // 4 components per row, 200px apart
|
||||
var/default_y = ((component_index - 1) / 4) * 150 + 50 // 150px between rows
|
||||
UNTYPED_LIST_ADD(assembly.component_positions, list(
|
||||
"ref" = REF(IC),
|
||||
"x" = default_x,
|
||||
"y" = default_y
|
||||
))
|
||||
|
||||
// Store component by its original index for wiring
|
||||
created_components["[component_data["i"]]"] = IC
|
||||
|
||||
return created_components
|
||||
|
||||
/**
|
||||
* Restores wiring connections between components (ultra-compact format only)
|
||||
*
|
||||
* @param assembly_data The deserialized assembly data
|
||||
* @param created_components List of created components indexed by original index
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/restore_component_wiring(list/assembly_data, list/created_components)
|
||||
if(!assembly_data["connections"] || !islist(assembly_data["connections"]))
|
||||
return
|
||||
|
||||
for(var/connection in assembly_data["connections"])
|
||||
if(!connection || !islist(connection))
|
||||
continue
|
||||
|
||||
// Support both old and new connection formats
|
||||
var/source_comp_index, source_pin_type, source_pin_index
|
||||
var/target_comp_index, target_pin_type, target_pin_index
|
||||
|
||||
// Try ultra-compact format first (new format)
|
||||
if(connection["sc"])
|
||||
source_comp_index = connection["sc"] // source_component
|
||||
source_pin_type = connection["spt"] // source_pin_type
|
||||
source_pin_index = connection["spi"] // source_pin_index
|
||||
target_comp_index = connection["tc"] // target_component
|
||||
target_pin_type = connection["tpt"] // target_pin_type
|
||||
target_pin_index = connection["tpi"] // target_pin_index
|
||||
|
||||
if(!source_comp_index || !target_comp_index || !source_pin_index || !target_pin_index)
|
||||
continue
|
||||
|
||||
var/source_key = "[source_comp_index]"
|
||||
var/target_key = "[target_comp_index]"
|
||||
|
||||
var/obj/item/integrated_circuit/source_IC = created_components[source_key]
|
||||
var/obj/item/integrated_circuit/target_IC = created_components[target_key]
|
||||
|
||||
if(!source_IC || !target_IC)
|
||||
continue
|
||||
|
||||
var/datum/integrated_io/source_pin
|
||||
var/datum/integrated_io/target_pin
|
||||
|
||||
// Get the appropriate pin based on single-letter type
|
||||
switch(source_pin_type)
|
||||
if("i")
|
||||
if(source_pin_index <= source_IC.inputs.len)
|
||||
source_pin = source_IC.inputs[source_pin_index]
|
||||
if("o")
|
||||
if(source_pin_index <= source_IC.outputs.len)
|
||||
source_pin = source_IC.outputs[source_pin_index]
|
||||
if("a")
|
||||
if(source_pin_index <= source_IC.activators.len)
|
||||
source_pin = source_IC.activators[source_pin_index]
|
||||
|
||||
switch(target_pin_type)
|
||||
if("i")
|
||||
if(target_pin_index <= target_IC.inputs.len)
|
||||
target_pin = target_IC.inputs[target_pin_index]
|
||||
if("o")
|
||||
if(target_pin_index <= target_IC.outputs.len)
|
||||
target_pin = target_IC.outputs[target_pin_index]
|
||||
if("a")
|
||||
if(target_pin_index <= target_IC.activators.len)
|
||||
target_pin = target_IC.activators[target_pin_index]
|
||||
|
||||
if(source_pin && target_pin)
|
||||
// Allow multiple outputs to connect to the same input
|
||||
// Only prevent truly identical connections (same source pin to same target pin)
|
||||
var/connection_exists = FALSE
|
||||
|
||||
// Check if this exact pin-to-pin connection already exists
|
||||
if(target_pin in source_pin.linked)
|
||||
connection_exists = TRUE
|
||||
|
||||
if(!connection_exists)
|
||||
source_pin.linked |= target_pin
|
||||
target_pin.linked |= source_pin
|
||||
|
||||
#undef ASSEMBLY_PREFIX
|
||||
#undef CIRCUIT_PREFIX
|
||||
@@ -12,11 +12,29 @@
|
||||
|
||||
var/upgraded = FALSE // When hit with an upgrade disk, will turn true, allowing it to print the higher tier circuits.
|
||||
var/illegal_upgraded = FALSE // When hit with an illegal upgrade disk, will turn true, allowing it to print the illegal circuits.
|
||||
var/can_clone = FALSE // Same for above, but will allow the printer to duplicate a specific assembly. (Not implemented)
|
||||
// var/static/list/recipe_list = list()
|
||||
var/obj/item/electronic_assembly/assembly_to_clone = null // Not implemented x3
|
||||
var/can_clone = FALSE // Same for above, but will allow the printer to duplicate a specific assembly.
|
||||
var/dirty_items = FALSE
|
||||
|
||||
// Printing state variables
|
||||
var/is_printing = FALSE // If true, printer is busy cloning.
|
||||
var/print_end_time = 0 // World time when printing will finish
|
||||
var/obj/item/electronic_assembly/queued_assembly = null // The assembly being cloned.
|
||||
|
||||
/obj/item/integrated_circuit_printer/proc/finish_printing()
|
||||
if(!queued_assembly)
|
||||
is_printing = FALSE
|
||||
return
|
||||
|
||||
// Drop the assembly on the ground
|
||||
queued_assembly.forceMove(get_turf(src))
|
||||
playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
|
||||
visible_message(span_notice("[src] beeps as it finishes printing '[queued_assembly.name]'."))
|
||||
|
||||
// Clear printing state
|
||||
queued_assembly = null
|
||||
is_printing = FALSE
|
||||
print_end_time = 0
|
||||
|
||||
/obj/item/integrated_circuit_printer/all_upgrades
|
||||
upgraded = TRUE
|
||||
illegal_upgraded = TRUE
|
||||
@@ -141,20 +159,20 @@
|
||||
|
||||
if(ispath(path, /obj/item/integrated_circuit))
|
||||
var/obj/item/integrated_circuit/IC = path
|
||||
if((initial(IC.spawn_flags) & IC_SPAWN_RESEARCH) && (!(initial(IC.spawn_flags) & IC_SPAWN_DEFAULT)) && !upgraded)
|
||||
if((IC::spawn_flags & IC_SPAWN_RESEARCH) && (!(IC::spawn_flags & IC_SPAWN_DEFAULT)) && !upgraded)
|
||||
can_build = FALSE
|
||||
|
||||
var/cost = 1
|
||||
if(ispath(path, /obj/item/electronic_assembly))
|
||||
var/obj/item/electronic_assembly/E = path
|
||||
cost = round((initial(E.max_complexity) + initial(E.max_components)) / 4)
|
||||
cost = round((E::max_complexity + E::max_components) / 4)
|
||||
else
|
||||
var/obj/item/I = path
|
||||
cost = initial(I.w_class)
|
||||
cost = I::w_class
|
||||
|
||||
items.Add(list(list(
|
||||
"name" = initial(O.name),
|
||||
"desc" = initial(O.desc),
|
||||
"name" = O::name,
|
||||
"desc" = O::desc,
|
||||
"can_build" = can_build,
|
||||
"cost" = cost,
|
||||
"path" = path,
|
||||
@@ -174,8 +192,9 @@
|
||||
data["metal_per_sheet"] = metal_per_sheet
|
||||
data["debug"] = debug
|
||||
data["upgraded"] = upgraded
|
||||
data["can_clone"] = can_clone
|
||||
data["assembly_to_clone"] = assembly_to_clone
|
||||
data["can_clone"] = can_clone && !is_printing // Can not clone while printing
|
||||
data["is_printing"] = is_printing
|
||||
data["print_time_remaining"] = is_printing ? max(0, print_end_time - world.time) : 0
|
||||
|
||||
return data
|
||||
|
||||
@@ -186,6 +205,17 @@
|
||||
add_fingerprint(ui.user)
|
||||
|
||||
switch(action)
|
||||
if("import_circuit")
|
||||
if(!can_clone)
|
||||
to_chat(ui.user, span_warning("This printer requires a clone upgrade disk to import circuit designs!"))
|
||||
return TRUE
|
||||
|
||||
if(is_printing) // Should not be possible to reach here.
|
||||
to_chat(ui.user, span_warning("The printer is busy! Please wait for the current print job to finish."))
|
||||
return TRUE
|
||||
|
||||
handle_circuit_import(ui.user)
|
||||
return TRUE
|
||||
if("build")
|
||||
var/build_type = text2path(params["build"])
|
||||
if(!build_type || !ispath(build_type))
|
||||
@@ -195,10 +225,10 @@
|
||||
|
||||
if(ispath(build_type, /obj/item/electronic_assembly))
|
||||
var/obj/item/electronic_assembly/E = build_type
|
||||
cost = round( (initial(E.max_complexity) + initial(E.max_components) ) / 4)
|
||||
cost = round( (E::max_complexity + E::max_components ) / 4)
|
||||
else
|
||||
var/obj/item/I = build_type
|
||||
cost = initial(I.w_class)
|
||||
cost = I::w_class
|
||||
|
||||
var/in_some_category = FALSE
|
||||
for(var/category in SScircuit.circuit_fabricator_recipe_list)
|
||||
@@ -221,6 +251,240 @@
|
||||
playsound(src, 'sound/items/jaws_pry.ogg', 50, TRUE)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Imports a circuit design from JSON data
|
||||
* This uses the same logic as the vrdb/html vore belly imports
|
||||
*
|
||||
* @param user The user importing the circuit
|
||||
* @param circuit_data JSON string containing the circuit data
|
||||
*/
|
||||
/obj/item/integrated_circuit_printer/proc/check_interactivity(mob/user)
|
||||
return user.Adjacent(src)
|
||||
|
||||
/obj/item/integrated_circuit_printer/proc/handle_circuit_import(mob/user)
|
||||
if(!user || user.stat || user.restrained() || !Adjacent(user))
|
||||
return
|
||||
|
||||
var/input_file = input(user, "Please choose a circuit JSON file to import.", "Import Circuit") as file
|
||||
if(!input_file)
|
||||
return
|
||||
|
||||
var/file_size = length(file2text(input_file))
|
||||
if(file_size > 1048576 / 4) // quarter of a megabyte.
|
||||
to_chat(user, span_warning("File too large! Circuit files must be smaller than 1MB. Your file is [num2text(file_size)] bytes."))
|
||||
return
|
||||
|
||||
if(file_size < 10)
|
||||
to_chat(user, span_warning("This doesn't appear to be a valid circuit file."))
|
||||
return
|
||||
|
||||
var/input_data
|
||||
try
|
||||
input_data = file2text(input_file)
|
||||
catch(var/exception/e)
|
||||
to_chat(user, span_warning("Failed to read file: [e]. Please ensure you selected a valid text/JSON file."))
|
||||
return
|
||||
|
||||
if(!input_data || length(input_data) < 10)
|
||||
to_chat(user, span_warning("The selected file is empty or unreadable. Please select a valid circuit JSON file."))
|
||||
return
|
||||
|
||||
// Basic JSON validation.
|
||||
if(!findtext(input_data, "{"))
|
||||
// If it doesn't contain basic JSON characters, it's likely not a JSON file
|
||||
to_chat(user, span_warning("Invalid file format! Please select a JSON file containing circuit data. (File appears to be binary or non-text format)"))
|
||||
return
|
||||
|
||||
// Additional validation to prevent binary files..
|
||||
if(findtext(input_data, "\xFF\xD8\xFF") || findtext(input_data, "\x89PNG") || findtext(input_data, "GIF89a") || findtext(input_data, "GIF87a"))
|
||||
to_chat(user, span_warning("Invalid file type! You selected an image file. Please select a JSON text file containing circuit data."))
|
||||
return
|
||||
|
||||
// Check if the input is Base64 encoded and decode it
|
||||
if(length(input_data) > 0 && !findtext(input_data, "{"))
|
||||
// If it doesn't contain '{' it's likely Base64 encoded JSON
|
||||
var/decoded_data = rustg_decode_base64(input_data)
|
||||
if(decoded_data && length(decoded_data) > 0)
|
||||
input_data = decoded_data
|
||||
else
|
||||
to_chat(user, span_warning("Unable to decode file data. Please select a valid circuit JSON file."))
|
||||
return
|
||||
|
||||
import_circuit(user, input_data, FALSE, null)
|
||||
|
||||
/obj/item/integrated_circuit_printer/proc/import_circuit(mob/user, circuit_data, override_type = FALSE, custom_type = null)
|
||||
if(!circuit_data)
|
||||
to_chat(user, span_warning("No circuit data provided!"))
|
||||
return
|
||||
|
||||
// Add safety check before deserializing
|
||||
if(length(circuit_data) > 100000) // Reduced from 50KB to be more conservative
|
||||
to_chat(user, span_warning("Circuit data is too large to process!"))
|
||||
return
|
||||
|
||||
// Additional safety checks for malformed data
|
||||
if(length(circuit_data) < 20) // Increase minimum size
|
||||
to_chat(user, span_warning("Circuit data is too small to be valid."))
|
||||
return
|
||||
|
||||
// Validate that this looks like circuit JSON data
|
||||
if(!findtext(circuit_data, "components") && !findtext(circuit_data, "assembly"))
|
||||
to_chat(user, span_warning("This doesn't appear to be valid circuit data."))
|
||||
return
|
||||
|
||||
// Deserialize the circuit data with enhanced error handling
|
||||
var/list/assembly_data = null
|
||||
try
|
||||
assembly_data = deserialize_electronic_assembly(circuit_data)
|
||||
catch(var/exception/e)
|
||||
to_chat(user, span_warning("Failed to process circuit data: [e]. The file may be corrupted or not a valid circuit export."))
|
||||
return
|
||||
|
||||
if(!assembly_data)
|
||||
to_chat(user, span_warning("Invalid circuit data! Please select a valid circuit export file (.json) created by the circuit export system."))
|
||||
return
|
||||
|
||||
// Validate that the assembly data has required fields
|
||||
if(!islist(assembly_data) || !assembly_data["components"])
|
||||
to_chat(user, span_warning("Invalid circuit format!"))
|
||||
return
|
||||
|
||||
// Check if we have enough metal to build all components
|
||||
var/total_cost = 0
|
||||
var/total_complexity = 0
|
||||
var/list/available_components = list()
|
||||
var/list/components_to_create = list()
|
||||
|
||||
// Build list of available components
|
||||
for(var/category in SScircuit.circuit_fabricator_recipe_list)
|
||||
if(category == "Illegal Parts" && !illegal_upgraded)
|
||||
continue
|
||||
var/list/circuit_list = SScircuit.circuit_fabricator_recipe_list[category]
|
||||
for(var/path in circuit_list)
|
||||
available_components += path
|
||||
|
||||
// Check each component and calculate costs
|
||||
for(var/list/component_data in assembly_data["components"])
|
||||
// Support both old "type" and new "t" format for component type
|
||||
var/component_type = component_data["type"] || component_data["t"]
|
||||
// Support both old "name" and new "n" format for component name
|
||||
var/component_name = component_data["name"] || component_data["n"] || "Unknown Component"
|
||||
|
||||
if(!component_type)
|
||||
to_chat(user, span_warning("Component missing type information. Skipping."))
|
||||
continue
|
||||
|
||||
// Handle both shortened and full paths flexibly
|
||||
var/build_type = null
|
||||
|
||||
build_type = text2path(component_type)
|
||||
if(!build_type || !ispath(build_type, /obj/item/integrated_circuit))
|
||||
// Try with circuit prefix (for new shortened paths)
|
||||
var/full_path = "/obj/item/integrated_circuit/[component_type]"
|
||||
build_type = text2path(full_path)
|
||||
|
||||
if(!build_type || !ispath(build_type, /obj/item/integrated_circuit))
|
||||
to_chat(user, span_warning("Unknown component type: [component_type]. Skipping."))
|
||||
continue
|
||||
|
||||
// Check if this component is available
|
||||
if(!(build_type in available_components))
|
||||
to_chat(user, span_warning("Component '[component_name]' ([build_type]) is not available in this printer. Skipping."))
|
||||
continue
|
||||
|
||||
// Check if component requires upgrades
|
||||
if(ispath(build_type, /obj/item/integrated_circuit))
|
||||
var/obj/item/integrated_circuit/IC = build_type
|
||||
var/spawn_flags = IC::spawn_flags
|
||||
// Component requires upgrades only if it has IC_SPAWN_RESEARCH but NOT IC_SPAWN_DEFAULT
|
||||
if((spawn_flags & IC_SPAWN_RESEARCH) && !(spawn_flags & IC_SPAWN_DEFAULT) && !upgraded)
|
||||
to_chat(user, span_warning("Component '[component_name]' requires printer upgrades. Skipping."))
|
||||
continue
|
||||
|
||||
// Calculate cost
|
||||
var/cost = 1
|
||||
if(ispath(build_type, /obj/item/electronic_assembly))
|
||||
var/obj/item/electronic_assembly/E = build_type
|
||||
cost = round((E::max_complexity + E::max_components) / 4)
|
||||
else
|
||||
var/obj/item/I = build_type
|
||||
cost = I::w_class
|
||||
|
||||
// Calculate complexity for printing time
|
||||
var/complexity = 1
|
||||
if(ispath(build_type, /obj/item/integrated_circuit))
|
||||
var/obj/item/integrated_circuit/IC = build_type
|
||||
complexity = IC::complexity
|
||||
|
||||
total_cost += cost
|
||||
total_complexity += complexity
|
||||
UNTYPED_LIST_ADD(components_to_create, list(
|
||||
"type" = build_type,
|
||||
"data" = component_data,
|
||||
"cost" = cost
|
||||
))
|
||||
|
||||
if(!LAZYLEN(components_to_create))
|
||||
to_chat(user, span_warning("No valid components found in the circuit data!"))
|
||||
return
|
||||
|
||||
// Check if we have enough metal
|
||||
if(!debug && (total_cost / 2) > metal)
|
||||
to_chat(user, span_warning("Not enough metal! Need [total_cost / 2] units, have [metal] units."))
|
||||
return
|
||||
|
||||
// Calculate assembly cost based on w_class (1 metal per size level)
|
||||
var/assembly_w_class = assembly_data["w_class"] || ITEMSIZE_SMALL // Default to SMALL if not specified
|
||||
var/assembly_cost = assembly_w_class * 10
|
||||
|
||||
total_cost += assembly_cost
|
||||
|
||||
// Final metal check with assembly cost
|
||||
if(!debug && (total_cost / 2) > metal)
|
||||
to_chat(user, span_warning("Not enough metal! Need [total_cost / 2] units (including assembly), have [metal] units."))
|
||||
return
|
||||
|
||||
if(!debug)
|
||||
metal = max(0, metal - (total_cost / 2))
|
||||
|
||||
// Calculate printing time based on actual complexity (1 minute per 120 complexity = 5 deciseconds per complexity)
|
||||
var/print_time = total_complexity * 5
|
||||
|
||||
// Create the assembly
|
||||
var/obj/item/electronic_assembly/assembly = create_assembly_from_data(assembly_data, override_type, custom_type)
|
||||
if(!assembly)
|
||||
to_chat(user, span_warning("Failed to create assembly!"))
|
||||
if(!debug)
|
||||
metal += total_cost
|
||||
return
|
||||
|
||||
// Add components to assembly
|
||||
var/list/created_components = add_components_to_assembly(assembly, assembly_data, available_components)
|
||||
if(!created_components || !LAZYLEN(created_components))
|
||||
to_chat(user, span_warning("Failed to add components to assembly! No components were created."))
|
||||
qdel(assembly)
|
||||
if(!debug)
|
||||
metal += total_cost
|
||||
return
|
||||
|
||||
// Restore wiring, and appearance
|
||||
restore_component_wiring(assembly_data, created_components)
|
||||
assembly.update_icon()
|
||||
|
||||
// Start the printing process
|
||||
queued_assembly = assembly
|
||||
is_printing = TRUE
|
||||
print_end_time = world.time + print_time
|
||||
|
||||
// Use addtimer instead of processing for efficiency
|
||||
addtimer(CALLBACK(src, PROC_REF(finish_printing)), print_time, TIMER_DELETE_ME)
|
||||
|
||||
var/print_minutes = round(print_time / 600, 0.1) // Convert to minutes for display
|
||||
to_chat(user, span_notice("Printing '[assembly.name]' with [LAZYLEN(created_components)] component\s. Estimated completion time: [print_minutes] minute\s."))
|
||||
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
|
||||
|
||||
return TRUE
|
||||
|
||||
// FUKKEN UPGRADE DISKS
|
||||
/obj/item/disk/integrated_circuit/upgrade
|
||||
name = "integrated circuit printer upgrade disk"
|
||||
@@ -241,7 +505,6 @@
|
||||
icon_state = "upgrade_disk_illegal"
|
||||
origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_ILLEGAL = 1)
|
||||
|
||||
// To be implemented later.
|
||||
/obj/item/disk/integrated_circuit/upgrade/clone
|
||||
name = "integrated circuit printer upgrade disk - circuit cloner"
|
||||
desc = "Install this into your integrated circuit printer to enhance it. This one allows the printer to duplicate assemblies."
|
||||
|
||||
@@ -24,6 +24,19 @@
|
||||
)
|
||||
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
|
||||
|
||||
/datum/design_techweb/custom_circuit_printer_upgrade_clone
|
||||
name = "Integrated circuit printer upgrade - circuit cloner"
|
||||
desc = "Allows the integrated circuit printer to clone existing circuit assemblies"
|
||||
id = "ic_printer_upgrade_clone"
|
||||
// req_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4)
|
||||
build_type = PROTOLATHE
|
||||
materials = list(MAT_STEEL = 2000)
|
||||
build_path = /obj/item/disk/integrated_circuit/upgrade/clone
|
||||
category = list(
|
||||
RND_CATEGORY_CIRCUITRY
|
||||
)
|
||||
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
|
||||
|
||||
/datum/design_techweb/wirer
|
||||
name = "Custom wirer tool"
|
||||
id = "wirer"
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
design_ids = list(
|
||||
"assembly-implant",
|
||||
"ic_printer_upgrade_adv",
|
||||
"ic_printer_upgrade_clone",
|
||||
)
|
||||
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
|
||||
|
||||
|
||||
@@ -290,10 +290,30 @@ const Circuit = (
|
||||
onPortRightClick,
|
||||
} = props;
|
||||
|
||||
const [pos, setPos] = useSharedState(`component-pos-${circuit.ref}`, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
const { act, data } = useBackend<Data>();
|
||||
|
||||
// Find stored position for this circuit
|
||||
const storedPosition = data.component_positions?.find(
|
||||
(pos) => pos.ref === circuit.ref,
|
||||
);
|
||||
const initialPosition = storedPosition
|
||||
? { x: storedPosition.x, y: storedPosition.y }
|
||||
: { x: 0, y: 0 };
|
||||
|
||||
const [pos, setPos] = useSharedState(
|
||||
`component-pos-${circuit.ref}`,
|
||||
initialPosition,
|
||||
);
|
||||
|
||||
const handleComponentMoved = (val) => {
|
||||
setPos(val);
|
||||
// Also notify the backend to track position for export/import
|
||||
act('update_component_position', {
|
||||
ref: circuit.ref,
|
||||
x: val.x,
|
||||
y: val.y,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<CircuitComponent
|
||||
@@ -301,7 +321,7 @@ const Circuit = (
|
||||
gridMode
|
||||
x={pos.x}
|
||||
y={pos.y}
|
||||
onComponentMoved={(val) => setPos(val)}
|
||||
onComponentMoved={handleComponentMoved}
|
||||
onPortUpdated={onPortUpdated}
|
||||
onPortLoaded={onPortLoaded}
|
||||
onPortMouseDown={onPortMouseDown}
|
||||
|
||||
@@ -35,6 +35,18 @@ export const ICAssembly = (props) => {
|
||||
onClick={() => act('rename')}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
color="transparent"
|
||||
width={2.5}
|
||||
height={2}
|
||||
textAlign="center"
|
||||
icon="file-export"
|
||||
tooltip="Export Circuit"
|
||||
tooltipPosition="bottom-start"
|
||||
onClick={() => act('export_circuit')}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
color="transparent"
|
||||
|
||||
@@ -7,6 +7,13 @@ export type Data = {
|
||||
battery_max: number;
|
||||
net_power: number;
|
||||
circuits: CircuitData[];
|
||||
component_positions: ComponentPosition[];
|
||||
};
|
||||
|
||||
export type ComponentPosition = {
|
||||
ref: string;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type CircuitData = {
|
||||
|
||||
63
tgui/packages/tgui/interfaces/ICExport.tsx
Normal file
63
tgui/packages/tgui/interfaces/ICExport.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Window } from 'tgui/layouts';
|
||||
import { Box, Button, Section, Stack, TextArea } from 'tgui-core/components';
|
||||
|
||||
type Data = {
|
||||
export_data: string;
|
||||
assembly_name: string;
|
||||
};
|
||||
|
||||
const downloadCircuitFile = (data: string, filename: string) => {
|
||||
const blob = new Blob([data], {
|
||||
type: 'application/json',
|
||||
});
|
||||
|
||||
Byond.saveBlob(blob, filename, '.json');
|
||||
};
|
||||
|
||||
export const ICExport = (props) => {
|
||||
const { data, act } = useBackend<Data>();
|
||||
const { export_data, assembly_name } = data;
|
||||
|
||||
const handleDownload = () => {
|
||||
const filename = assembly_name
|
||||
? `circuit_${assembly_name}`
|
||||
: 'circuit_export';
|
||||
downloadCircuitFile(export_data, filename);
|
||||
};
|
||||
|
||||
return (
|
||||
<Window width={600} height={350}>
|
||||
<Window.Content>
|
||||
<Section title="Circuit Export Data">
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<TextArea
|
||||
height="40vh"
|
||||
width="100%"
|
||||
value={export_data || 'No data available'}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Stack>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon="download"
|
||||
content="Download as File"
|
||||
onClick={handleDownload}
|
||||
tooltip="Download circuit data as a JSON file"
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
Note: You can download the circuit as a file or copy to clipboard.
|
||||
Use the circuit printer's import function to load the
|
||||
circuit.
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -33,7 +33,7 @@ type item = {
|
||||
};
|
||||
|
||||
export const ICPrinter = (props) => {
|
||||
const { data } = useBackend<Data>();
|
||||
const { data, act } = useBackend<Data>();
|
||||
|
||||
const { metal, max_metal, metal_per_sheet, upgraded, can_clone } = data;
|
||||
|
||||
@@ -43,11 +43,13 @@ export const ICPrinter = (props) => {
|
||||
<Stack fill vertical>
|
||||
<Stack.Item>
|
||||
<Section fill title="Status">
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Metal">
|
||||
<ProgressBar value={metal} maxValue={max_metal}>
|
||||
{metal / metal_per_sheet} / {max_metal / metal_per_sheet}{' '}
|
||||
sheets
|
||||
{metal / metal_per_sheet} /{' '}
|
||||
{max_metal / metal_per_sheet} sheets
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Circuits Available">
|
||||
@@ -57,10 +59,27 @@ export const ICPrinter = (props) => {
|
||||
{can_clone ? 'Available' : 'Unavailable'}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Box mt={1}>
|
||||
</Stack.Item>
|
||||
{!!can_clone && (
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon="file-import"
|
||||
content="Import Circuit"
|
||||
onClick={() => {
|
||||
act('import_circuit', {
|
||||
override_type: false,
|
||||
custom_type: null,
|
||||
});
|
||||
}}
|
||||
tooltip="Import a circuit design from a JSON file"
|
||||
/>
|
||||
</Stack.Item>
|
||||
)}
|
||||
<Stack.Item>
|
||||
Note: A red component name means that the printer must be
|
||||
upgraded to create that component.
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
|
||||
@@ -3011,6 +3011,7 @@
|
||||
#include "code\modules\integrated_electronics\core\integrated_circuit.dm"
|
||||
#include "code\modules\integrated_electronics\core\pins.dm"
|
||||
#include "code\modules\integrated_electronics\core\printer.dm"
|
||||
#include "code\modules\integrated_electronics\core\circuit_serialization.dm"
|
||||
#include "code\modules\integrated_electronics\core\tools.dm"
|
||||
#include "code\modules\integrated_electronics\core\assemblies\circuit_bug.dm"
|
||||
#include "code\modules\integrated_electronics\core\assemblies\clothing.dm"
|
||||
|
||||
Reference in New Issue
Block a user