mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-04-03 02:21:00 +01:00
* Begin clickcode attack_self fix Begins the work to make everything call back to parent for attack_self so that signals are sacred. * Makes MORE things call the attack_self() parent Yes, I could make special_handling a var on obj/item HOWEVER i want it to be specific so it can be tracked down later and ONLY the objects that use it can be refactored instead of sitting there literally forever and it just becoming 'a thing'. * Finishes making the rest of attack_self call parent. As mentioned, things such as 'specialty_goggles' 'special_handling' and the such are only there to help with attack_self until the attack_self is recoded for those items. * begone foul demon * some more cleanup * These * GOD this was annoying * yeh * Fix this * fLARES * Thesee too * toys! * Even more! * More fixes * Even more * rest of em * these too * Update syndie.dm * hardref clear * Update code/game/gamemodes/nuclear/pinpointer.dm * Update code/game/objects/effects/mines.dm * Update code/game/objects/items/blueprints_vr.dm * Update code/game/objects/items/blueprints_vr.dm * Update code/game/objects/items/contraband_vr.dm * Update code/game/objects/items/crayons.dm * Update code/game/objects/items/crayons.dm * Update code/game/objects/items/gunbox.dm * Update code/game/objects/items/gunbox.dm * Update code/game/objects/items/gunbox_vr.dm * Update code/game/objects/items/gunbox_vr.dm * Update code/game/objects/items/weapons/gift_wrappaper.dm * Update code/game/objects/items/crayons.dm * Update code/game/objects/items/crayons.dm * Update code/game/objects/items/gunbox.dm * these too * Update maintpanel_stack.dm * angry warning * Fixes packaged snacks. Fixes improper var default. * Special handling for these * proper poly types * Fixes magclaws Makes the 'features' it had just part of base magboots that can be adjusted via varswap. * Fixes jackets Fixes https://github.com/VOREStation/VOREStation/issues/18941 * Small bugfix Makes p_Theyre properly capitialize Makes examine show proper wording * Update gift_wrappaper.dm
523 lines
18 KiB
Plaintext
523 lines
18 KiB
Plaintext
|
|
/obj/item/integrated_circuit_printer
|
|
name = "integrated circuit printer"
|
|
desc = "A portable(ish) machine made to print tiny modular circuitry out of metal."
|
|
icon = 'icons/obj/integrated_electronics/electronic_tools.dmi'
|
|
icon_state = "circuit_printer"
|
|
w_class = ITEMSIZE_LARGE
|
|
var/metal = 0
|
|
var/max_metal = 100
|
|
var/metal_per_sheet = 10 // One sheet equals this much metal.
|
|
var/debug = FALSE // If true, metal is infinite.
|
|
|
|
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.
|
|
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
|
|
can_clone = TRUE
|
|
|
|
/obj/item/integrated_circuit_printer/illegal
|
|
illegal_upgraded = TRUE
|
|
can_clone = TRUE
|
|
|
|
/obj/item/integrated_circuit_printer/upgraded
|
|
upgraded = TRUE
|
|
can_clone = TRUE
|
|
|
|
/obj/item/integrated_circuit_printer/debug
|
|
name = "fractal integrated circuit printer"
|
|
desc = "A portable(ish) machine that makes modular circuitry seemingly out of thin air."
|
|
upgraded = TRUE
|
|
illegal_upgraded = TRUE
|
|
can_clone = TRUE
|
|
debug = TRUE
|
|
|
|
/obj/item/integrated_circuit_printer/attack_robot(mob/user as mob)
|
|
if(Adjacent(user))
|
|
return tgui_interact(user)
|
|
else
|
|
return ..()
|
|
|
|
/obj/item/integrated_circuit_printer/attackby(var/obj/item/O, var/mob/user)
|
|
if(istype(O,/obj/item/stack/material))
|
|
var/obj/item/stack/material/stack = O
|
|
if(stack.material.name == MAT_STEEL)
|
|
if(debug)
|
|
to_chat(user, span_warning("\The [src] does not need any material."))
|
|
return
|
|
var/num = min((max_metal - metal) / metal_per_sheet, stack.get_amount())
|
|
if(num < 1)
|
|
to_chat(user, span_warning("\The [src] is too full to add more metal."))
|
|
return
|
|
if(stack.use(max(1, round(num)))) // We don't want to create stacks that aren't whole numbers
|
|
to_chat(user, span_notice("You add [num] sheet\s to \the [src]."))
|
|
metal += num * metal_per_sheet
|
|
attack_self(user)
|
|
return TRUE
|
|
|
|
if(istype(O,/obj/item/integrated_circuit))
|
|
to_chat(user, span_notice("You insert the circuit into \the [src]."))
|
|
user.unEquip(O)
|
|
metal = min(metal + O.w_class, max_metal)
|
|
qdel(O)
|
|
attack_self(user)
|
|
return TRUE
|
|
|
|
if(istype(O,/obj/item/disk/integrated_circuit/upgrade/advanced))
|
|
if(upgraded)
|
|
to_chat(user, span_warning("\The [src] already has this upgrade."))
|
|
return TRUE
|
|
to_chat(user, span_notice("You install \the [O] into \the [src]."))
|
|
upgraded = TRUE
|
|
dirty_items = TRUE
|
|
attack_self(user)
|
|
return TRUE
|
|
|
|
if(istype(O,/obj/item/disk/integrated_circuit/upgrade/illegal))
|
|
if(illegal_upgraded)
|
|
to_chat(user, span_warning("\The [src] already has this upgrade."))
|
|
return TRUE
|
|
to_chat(user, span_notice("You install \the [O] into \the [src]."))
|
|
illegal_upgraded = TRUE
|
|
dirty_items = TRUE
|
|
attack_self(user)
|
|
return TRUE
|
|
|
|
if(istype(O,/obj/item/disk/integrated_circuit/upgrade/clone))
|
|
if(can_clone)
|
|
to_chat(user, span_warning("\The [src] already has this upgrade."))
|
|
return TRUE
|
|
to_chat(user, span_notice("You install \the [O] into \the [src]."))
|
|
can_clone = TRUE
|
|
attack_self(user)
|
|
return TRUE
|
|
|
|
return ..()
|
|
|
|
/obj/item/integrated_circuit_printer/vv_edit_var(var_name, var_value)
|
|
// Gotta update the static data in case an admin VV's the upgraded var for some reason..!
|
|
if(var_name == "upgraded")
|
|
dirty_items = TRUE
|
|
return ..()
|
|
|
|
/obj/item/integrated_circuit_printer/attack_self(mob/user)
|
|
. = ..(user)
|
|
if(.)
|
|
return TRUE
|
|
tgui_interact(user)
|
|
|
|
/obj/item/integrated_circuit_printer/tgui_state(mob/user)
|
|
return GLOB.tgui_physical_state
|
|
|
|
/obj/item/integrated_circuit_printer/tgui_interact(mob/user, datum/tgui/ui)
|
|
if(dirty_items)
|
|
update_tgui_static_data(user, ui)
|
|
dirty_items = FALSE
|
|
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "ICPrinter", name) // 500, 600
|
|
ui.open()
|
|
|
|
/obj/item/integrated_circuit_printer/tgui_static_data(mob/user)
|
|
var/list/data = ..()
|
|
|
|
var/list/categories = list()
|
|
for(var/category in SScircuit.circuit_fabricator_recipe_list)
|
|
var/list/cat_obj = list(
|
|
"name" = category,
|
|
"items" = null
|
|
)
|
|
if(cat_obj["name"] == "Illegal Parts" && !illegal_upgraded)
|
|
continue
|
|
var/list/circuit_list = SScircuit.circuit_fabricator_recipe_list[category]
|
|
var/list/items = list()
|
|
for(var/path in circuit_list)
|
|
var/obj/O = path
|
|
var/can_build = TRUE
|
|
|
|
if(ispath(path, /obj/item/integrated_circuit))
|
|
var/obj/item/integrated_circuit/IC = path
|
|
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((E::max_complexity + E::max_components) / 4)
|
|
else
|
|
var/obj/item/I = path
|
|
cost = I::w_class
|
|
|
|
items.Add(list(list(
|
|
"name" = O::name,
|
|
"desc" = O::desc,
|
|
"can_build" = can_build,
|
|
"cost" = cost,
|
|
"path" = path,
|
|
)))
|
|
|
|
cat_obj["items"] = items
|
|
categories.Add(list(cat_obj))
|
|
data["categories"] = categories
|
|
|
|
return data
|
|
|
|
/obj/item/integrated_circuit_printer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
|
var/list/data = ..()
|
|
|
|
data["metal"] = metal
|
|
data["max_metal"] = max_metal
|
|
data["metal_per_sheet"] = metal_per_sheet
|
|
data["debug"] = debug
|
|
data["upgraded"] = upgraded
|
|
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
|
|
|
|
/obj/item/integrated_circuit_printer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
|
if(..())
|
|
return TRUE
|
|
|
|
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))
|
|
return 1
|
|
|
|
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
|
|
|
|
var/in_some_category = FALSE
|
|
for(var/category in SScircuit.circuit_fabricator_recipe_list)
|
|
if(build_type in SScircuit.circuit_fabricator_recipe_list[category])
|
|
in_some_category = TRUE
|
|
break
|
|
if(!in_some_category)
|
|
return
|
|
|
|
if(!debug)
|
|
if(!Adjacent(ui.user))
|
|
to_chat(ui.user, span_notice("You are too far away from \the [src]."))
|
|
if(metal - cost < 0)
|
|
to_chat(ui.user, span_warning("You need [cost] metal to build that!."))
|
|
return 1
|
|
metal -= cost
|
|
var/obj/item/built = new build_type(get_turf(loc))
|
|
ui.user.put_in_hands(built)
|
|
to_chat(ui.user, span_notice("[capitalize(built.name)] printed."))
|
|
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
|
|
|
|
if(assembly_data["t"])
|
|
var/restored_path = src.restore_assembly_prefix(assembly_data["t"])
|
|
var/assembly_path = text2path(restored_path)
|
|
if(assembly_path && ispath(assembly_path, /obj/item/electronic_assembly/clothing))
|
|
to_chat(user, span_warning("Cannot import wearable electronic assemblies!"))
|
|
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"
|
|
desc = "Install this into your integrated circuit printer to enhance it."
|
|
icon = 'icons/obj/integrated_electronics/electronic_tools.dmi'
|
|
icon_state = "upgrade_disk"
|
|
item_state = "card-id"
|
|
w_class = ITEMSIZE_SMALL
|
|
origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4)
|
|
|
|
/obj/item/disk/integrated_circuit/upgrade/advanced
|
|
name = "integrated circuit printer upgrade disk - advanced designs"
|
|
desc = "Install this into your integrated circuit printer to enhance it. This one adds new, advanced designs to the printer."
|
|
|
|
/obj/item/disk/integrated_circuit/upgrade/illegal
|
|
name = "integrated circuit printer upgrade disk - illegal designs"
|
|
desc = "Install this into your integrated circuit printer to enhance it. This one adds new, but illegal designs to the printer."
|
|
icon_state = "upgrade_disk_illegal"
|
|
origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_ILLEGAL = 1)
|
|
|
|
/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."
|
|
icon_state = "upgrade_disk_clone"
|
|
origin_tech = list(TECH_ENGINEERING = 5, TECH_DATA = 6)
|