Hardware rework

- Hardware pieces are now items, rather than datums.
- Adds deconstruction for computers. Empty tablet/laptop/console frames may be wrenched to break them back into metal sheets. You have to empty the frame first, by using screwdriver to take out components one by one.
- Components may be moved between devices. You can for example take your tablet, remove it's hard drive, and slot it into a console. It will have all the files it had on your tablet.
- Not all hardware can be fitted into all devices. Tablet can't hold 2K GQ cluster hard drive, for example.
- Hardware may be fabricated by research for relatively low costs, once you have relevant research levels. Obtaining computer this way is much cheaper than buying it at the vendor.
- Data crystals added (glorified USB flash sticks) that allow file transfer to different devices. File browser program updated accordingly to support importing/exporting of files to these crystals.
- Battery module added.  These are wrappers for actual power cell object and act as limit for cell size, otherwise it would be possible to have 30k cells inside devices, which would allow them to run insanely long.
This commit is contained in:
Atlantis
2015-12-02 10:32:49 +01:00
parent 0dda785c50
commit 7e39ef1a38
27 changed files with 779 additions and 203 deletions
@@ -6,7 +6,7 @@
name = "Modular Microcomputer"
desc = "A small portable microcomputer"
var/enabled = 1 // Whether the computer is turned on.
var/enabled = 0 // Whether the computer is turned on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/hardware_flag = 0 // A flag that describes this device type
@@ -23,14 +23,19 @@
icon_state = "laptop-open"
var/icon_state_unpowered = null // Icon state when the computer is turned off
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
// Important hardware (must be installed for computer to work)
var/datum/computer_hardware/network_card/network_card // Network Card component of this computer. Allows connection to NTNet
var/datum/computer_hardware/hard_drive/hard_drive // Hard Drive component of this computer. Stores programs and files.
var/obj/item/weapon/cell/battery = null // Battery component of this computer. Powers the computer. Áll computers can have batteries.
var/obj/item/weapon/computer_hardware/network_card/network_card // Network Card component of this computer. Allows connection to NTNet
var/obj/item/weapon/computer_hardware/hard_drive/hard_drive // Hard Drive component of this computer. Stores programs and files.
var/obj/item/weapon/computer_hardware/battery_module/battery_module // An internal power source for this computer. Can be recharged.
// Optional hardware (improves functionality, but is not critical for computer to work)
var/datum/computer_hardware/card_slot/card_slot // ID Card slot component of this computer. Mostly for HoP modification console that needs ID slot for modification.
var/datum/computer_hardware/nano_printer/nano_printer // Nano Printer component of this computer, for your everyday paperwork needs.
var/obj/item/weapon/computer_hardware/card_slot/card_slot // ID Card slot component of this computer. Mostly for HoP modification console that needs ID slot for modification.
var/obj/item/weapon/computer_hardware/nano_printer/nano_printer // Nano Printer component of this computer, for your everyday paperwork needs.
var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive // Portable data storage
// Eject ID card from computer, if it has ID slot with card inside.
/obj/item/modular_computer/verb/eject_id()
@@ -64,7 +69,6 @@
card_slot.stored_card = null
user << "You remove the card from \the [src]"
// TODO: Convert hardware creation specific stuff to vending machine that handles laptops.
/obj/item/modular_computer/New()
processing_objects.Add(src)
update_icon()
@@ -76,8 +80,8 @@
qdel(network_card)
if(hard_drive)
qdel(hard_drive)
if(battery)
qdel(battery)
if(battery_module)
qdel(battery_module)
if(nano_printer)
qdel(nano_printer)
if(card_slot)
@@ -105,7 +109,7 @@
if(ui)
ui.close()
return 0
if((!battery || !battery.charge) && !check_power_override())
if((!battery_module || !battery_module.battery.charge) && !check_power_override())
if(ui)
ui.close()
return 0
@@ -145,7 +149,7 @@
/obj/item/modular_computer/attack_self(mob/user)
if(enabled)
ui_interact(user)
else if((battery && battery.charge) || check_power_override()) // Battery-run and charged or non-battery but powered by APC.
else if((battery_module && battery_module.battery.charge) || check_power_override()) // Battery-run and charged or non-battery but powered by APC.
user << "You press the power button and start up \the [src]"
enabled = 1
update_icon()
@@ -174,8 +178,8 @@
/obj/item/modular_computer/proc/get_header_data()
var/list/data = list()
if(battery)
switch(battery.percent())
if(battery_module)
switch(battery_module.battery.percent())
if(80 to 200) // 100 should be maximal but just in case..
data["PC_batteryicon"] = "batt_100.gif"
if(60 to 80)
@@ -188,12 +192,12 @@
data["PC_batteryicon"] = "batt_20.gif"
else
data["PC_batteryicon"] = "batt_5.gif"
data["PC_batterypercent"] = "[round(battery.percent())] %"
data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %"
data["PC_showbatteryicon"] = 1
else
data["PC_batteryicon"] = "batt_5.gif"
data["PC_batterypercent"] = "N/C"
data["PC_showbatteryicon"] = battery ? 1 : 0
data["PC_showbatteryicon"] = battery_module ? 1 : 0
switch(get_ntnet_status())
if(0)
@@ -210,12 +214,6 @@
data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen
return data
// Installs programs necessary for computer function.
/obj/item/modular_computer/proc/install_default_programs()
hard_drive.store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
hard_drive.store_file(new/datum/computer_file/program/ntnetdownload(src)) // NTNet Downloader Utility, allows users to download more software from NTNet repository
hard_drive.store_file(new/datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation.
// Relays kill program request to currently active program. Use this to quit current program.
/obj/item/modular_computer/proc/kill_program(var/forced = 0)
if(active_program)
@@ -253,17 +251,18 @@
kill_program()
return
if( href_list["PC_enable_component"] )
var/datum/computer_hardware/H = find_hardware_by_name(href_list["PC_enable_component"])
var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(href_list["PC_enable_component"])
if(H && istype(H) && !H.enabled)
H.enabled = 1
return
if( href_list["PC_disable_component"] )
var/datum/computer_hardware/H = find_hardware_by_name(href_list["PC_disable_component"])
var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(href_list["PC_disable_component"])
if(H && istype(H) && H.enabled)
H.enabled = 0
return
if( href_list["PC_shutdown"] )
shutdown_computer()
return
if( href_list["PC_runprogram"] )
var/prog = href_list["PC_runprogram"]
var/datum/computer_file/program/P = null
@@ -297,7 +296,7 @@
// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged
/obj/item/modular_computer/proc/handle_power()
if(!battery || battery.charge <= 0) // Battery-run but battery is depleted.
if(!battery_module || battery_module.battery.charge <= 0) // Battery-run but battery is depleted.
power_failure()
return 0
@@ -308,8 +307,8 @@
if(hard_drive && hard_drive.enabled)
power_usage += hard_drive.power_usage
if(battery)
battery.use(power_usage * CELLRATE)
if(battery_module)
battery_module.battery.use(power_usage * CELLRATE)
last_power_usage = power_usage
/obj/item/modular_computer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
@@ -332,11 +331,129 @@
if(!nano_printer)
return
nano_printer.load_paper(P)
if(istype(W, /obj/item/weapon/computer_hardware))
var/obj/item/weapon/computer_hardware/C = W
if(C.hardware_size <= max_hardware_size)
try_install_component(user, C)
else
user << "This component is too large for \the [src]."
if(istype(W, /obj/item/weapon/wrench))
var/list/components = get_all_components()
if(components.len)
user << "Remove all components from \the [src] before disassembling it."
return
new /obj/item/stack/material/steel( get_turf(src.loc), steel_sheet_cost )
src.visible_message("\The [src] has been disassembled by [user].")
if(istype(W, /obj/item/weapon/screwdriver))
var/list/all_components = get_all_components()
if(!all_components.len)
user << "This device doesn't have any components installed."
return
var/list/component_names = list()
for(var/obj/item/weapon/computer_hardware/H in all_components)
component_names.Add(H.name)
var/choice = input(usr, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names
if(!choice)
return
if(!Adjacent(usr))
return
var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(choice)
if(!H)
return
uninstall_component(user, H)
return
..()
// Attempts to install the hardware into apropriate slot.
/obj/item/modular_computer/proc/try_install_component(var/mob/living/user, var/obj/item/weapon/computer_hardware/H, var/found = 0)
// "USB" flash drive.
if(istype(H, /obj/item/weapon/computer_hardware/hard_drive/portable))
if(portable_drive)
user << "This computer's portable drive slot is already occupied by \the [portable_drive]."
return
found = 1
portable_drive = H
else if(istype(H, /obj/item/weapon/computer_hardware/hard_drive))
if(hard_drive)
user << "This computer's hard drive slot is already occupied by \the [hard_drive]."
return
found = 1
hard_drive = H
else if(istype(H, /obj/item/weapon/computer_hardware/network_card))
if(network_card)
user << "This computer's network card slot is already occupied by \the [network_card]."
return
found = 1
network_card = H
else if(istype(H, /obj/item/weapon/computer_hardware/nano_printer))
if(nano_printer)
user << "This computer's nano printer slot is already occupied by \the [nano_printer]."
return
found = 1
nano_printer = H
else if(istype(H, /obj/item/weapon/computer_hardware/card_slot))
if(card_slot)
user << "This computer's card slot is already occupied by \the [card_slot]."
return
found = 1
card_slot = H
else if(istype(H, /obj/item/weapon/computer_hardware/battery_module))
if(battery_module)
user << "This computer's battery slot is already occupied by \the [battery_module]."
return
found = 1
battery_module = H
if(found)
user << "You install \the [H] into \the [src]"
H.holder2 = src
user.drop_from_inventory(H)
H.forceMove(src)
// Uninstalls component. Found and Critical vars may be passed by parent types, if they have additional hardware.
/obj/item/modular_computer/proc/uninstall_component(var/mob/living/user, var/obj/item/weapon/computer_hardware/H, var/found = 0, var/critical = 0)
if(portable_drive == H)
portable_drive = null
found = 1
if(hard_drive == H)
hard_drive = null
found = 1
critical = 1
if(network_card == H)
network_card = null
found = 1
if(nano_printer == H)
nano_printer = null
found = 1
if(card_slot == H)
card_slot = null
found = 1
if(battery_module == H)
battery_module = null
found = 1
if(found)
user << "You remove \the [H] from \the [src]."
H.forceMove(get_turf(src))
H.holder2 = null
if(critical)
user << "<span class='danger'>\The [src]'s screen freezes for few seconds and then displays an \"HARDWARE ERROR: Critical component disconnected. Please verify component connection and reboot the device. If the problem persists contact technical support for assistance.\" warning.</span>"
kill_program(1)
enabled = 0
update_icon()
// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null
/obj/item/modular_computer/proc/find_hardware_by_name(var/name)
if(portable_drive && (portable_drive.name == name))
return portable_drive
if(hard_drive && (hard_drive.name == name))
return hard_drive
if(network_card && (network_card.name == name))
@@ -345,4 +462,23 @@
return nano_printer
if(card_slot && (card_slot.name == name))
return card_slot
return null
if(battery_module && (battery_module.name == name))
return battery_module
return null
// Returns list of all components
/obj/item/modular_computer/proc/get_all_components()
var/list/all_components = list()
if(hard_drive)
all_components.Add(hard_drive)
if(network_card)
all_components.Add(network_card)
if(portable_drive)
all_components.Add(portable_drive)
if(nano_printer)
all_components.Add(nano_printer)
if(card_slot)
all_components.Add(card_slot)
if(battery_module)
all_components.Add(battery_module)
return all_components
@@ -32,9 +32,11 @@
machinery_computer = comp
machinery_computer.cpu = src
hardware_flag = machinery_computer.hardware_flag
max_hardware_size = machinery_computer.max_hardware_size
steel_sheet_cost = machinery_computer.steel_sheet_cost
/obj/item/modular_computer/processor/find_hardware_by_name(var/N)
var/datum/computer_hardware/H = machinery_computer.find_hardware_by_name(N)
var/obj/item/weapon/computer_hardware/H = machinery_computer.find_hardware_by_name(N)
if(H)
return H
else
@@ -71,4 +73,40 @@
visible_message("\The [machinery_computer] shuts down.")
enabled = 0
machinery_computer.update_icon()
return
return
// Tesla links only work on machinery types, so we'll override the default try_install_component() proc
/obj/item/modular_computer/processor/try_install_component(var/mob/living/user, var/obj/item/weapon/computer_hardware/H, var/found = 0)
if(istype(H, /obj/item/weapon/computer_hardware/tesla_link))
if(machinery_computer.tesla_link)
user << "This computer's tesla link slot is already occupied by \the [machinery_computer.tesla_link]."
return
var/obj/item/weapon/computer_hardware/tesla_link/L = H
L.holder = machinery_computer
machinery_computer.tesla_link = L
// Consoles don't usually have internal power source, so we can't disable tesla link in them.
if(istype(machinery_computer, /obj/machinery/modular_computer/console))
L.critical = 1
found = 1
..(user, H, found)
/obj/item/modular_computer/processor/uninstall_component(var/mob/living/user, var/obj/item/weapon/computer_hardware/H, var/found = 0, var/critical = 0)
if(machinery_computer.tesla_link == H)
machinery_computer.tesla_link = null
var/obj/item/weapon/computer_hardware/tesla_link/L = H
L.critical = 0 // That way we can install tesla link from console to laptop and it will be possible to turn it off via config.
L.holder = null
found = 1
..(user, H, found, critical)
/obj/item/modular_computer/processor/get_all_components()
var/list/all_components = ..()
if(machinery_computer.tesla_link)
all_components.Add(machinery_computer.tesla_link)
return all_components
// Perform adjacency checks on our machinery counterpart, rather than on ourselves.
/obj/item/modular_computer/processor/Adjacent(var/atom/neighbor)
if(!machinery_computer)
return 0
return machinery_computer.Adjacent(neighbor)
@@ -4,4 +4,5 @@
icon_state = "tablet"
icon_state_unpowered = "tablet"
icon_state_menu = "menu"
hardware_flag = PROGRAM_TABLET
hardware_flag = PROGRAM_TABLET
max_hardware_size = 1
@@ -4,10 +4,10 @@
name = "modular computer"
desc = "An advanced computer"
var/battery_powered = 0 // Whether computer should be battery powered. It is set automatically
var/battery_powered = 0 // Whether computer should be battery powered. It is set automatically
use_power = 0
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0 // Power usage during last tick
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0 // Power usage during last tick
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
@@ -15,24 +15,19 @@
icon = null
icon_state = null
var/icon_state_unpowered = null // Icon state when the computer is turned off
var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/keyboard_icon_state_menu = "keyboard1" // Keyboard's icon state overlay when the computer is turned on and no program is loaded
var/nokeyboard = 0 // Set to 1 to disable keyboard icons for this subtype.
var/icon_state_unpowered = null // Icon state when the computer is turned off
var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/keyboard_icon_state_menu = "keyboard1" // Keyboard's icon state overlay when the computer is turned on and no program is loaded
var/nokeyboard = 0 // Set to 1 to disable keyboard icons for this subtype.
var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
var/steel_sheet_cost = 10 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
// Important hardware (must be installed for computer to work)
var/datum/computer_hardware/network_card/network_card // Network Card component of this computer. Allows connection to NTNet
var/datum/computer_hardware/hard_drive/hard_drive // Hard Drive component of this computer. Stores programs and files.
var/obj/item/weapon/cell/battery = null // Battery component of this computer. Powers the computer. Áll computers can have batteries.
// Optional hardware (improves functionality, but is not critical for computer to work)
var/datum/computer_hardware/tesla_link/tesla_link // Tesla Link component of this computer. Allows remote charging from nearest APC.
var/datum/computer_hardware/card_slot/card_slot // ID Card slot component of this computer. Mostly for HoP modification console that needs ID slot for modification.
var/datum/computer_hardware/nano_printer/nano_printer // Nano Printer component of this computer, for your everyday paperwork needs.
var/obj/item/weapon/computer_hardware/tesla_link/tesla_link // Tesla Link component of this computer. Allows remote charging from nearest APC.
var/obj/item/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things.
var/obj/item/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things.
/obj/machinery/modular_computer/update_icon()
icon_state = icon_state_unpowered
@@ -62,12 +57,6 @@
..()
cpu = new(src)
// Installs programs necessary for computer function.
// TODO: Implement program for downloading of other programs, and replace hardcoded program addition here
/obj/machinery/modular_computer/proc/install_default_programs()
if(cpu && cpu.hard_drive)
cpu.install_default_programs()
// On-click handling. Turns on the computer if it's off and opens the GUI.
/obj/machinery/modular_computer/attack_hand(mob/user)
if(cpu)
@@ -89,7 +78,7 @@
// Used in following function to reduce copypaste
/obj/machinery/modular_computer/proc/power_failure()
if(cpu && cpu.enabled) // Shut down the computer
visible_message("<span class='danger'>\The [src]'s screen flickers [battery ? "\"BATTERY CRITICAL\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.</span>")
visible_message("<span class='danger'>\The [src]'s screen flickers [cpu.battery_module ? "\"BATTERY CRITICAL\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.</span>")
if(cpu)
cpu.kill_program(1)
cpu.enabled = 0
@@ -99,16 +88,16 @@
// Called by cpu item's process() automatically, handles our power interaction.
/obj/machinery/modular_computer/proc/handle_power()
if(cpu.battery && cpu.battery.charge <= 0) // Battery-run but battery is depleted.
if(cpu.battery_module && cpu.battery_module.battery.charge <= 0) // Battery-run but battery is depleted.
power_failure()
return 0
else if(!cpu.battery && (!powered() || !tesla_link || !tesla_link.enabled)) // Not battery run, but lacking APC connection.
else if(!cpu.battery_module && (!powered() || !tesla_link || !tesla_link.enabled)) // Not battery run, but lacking APC connection.
power_failure()
return 0
else if(stat & NOPOWER)
stat &= ~NOPOWER
if(cpu.battery && cpu.battery.charge)
if(cpu.battery_module && cpu.battery_module.battery.charge)
battery_powered = 1
else
battery_powered = 0
@@ -132,18 +121,18 @@
active_power_usage = idle_power_usage + 100 // APCLink only charges at 100W rate, but covers any power usage.
use_power = 1
// Battery is not fully charged. Begin slowly recharging.
if(cpu.battery && cpu.battery.charge < cpu.battery.maxcharge)
if(cpu.battery_module && (cpu.battery_module.battery.charge < cpu.battery_module.battery.maxcharge))
use_power = 2
if(cpu.battery && powered() && (use_power == 2)) // Battery charging itself
cpu.battery.give(100 * CELLRATE)
else if(cpu.battery && !powered()) // Unpowered, but battery covers the usage.
cpu.battery.use(idle_power_usage * CELLRATE)
if(cpu.battery_module && powered() && (use_power == 2)) // Battery charging itself
cpu.battery_module.battery.give(100 * CELLRATE)
else if(cpu.battery_module && !powered()) // Unpowered, but battery covers the usage.
cpu.battery_module.battery.use(idle_power_usage * CELLRATE)
else // No wireless connection run only on battery.
use_power = 0
if (cpu.battery)
cpu.battery.use(power_usage * CELLRATE)
if (cpu.battery_module)
cpu.battery_module.battery.use(power_usage * CELLRATE)
cpu.last_power_usage = power_usage
// Modular computers can have battery in them, we handle power in previous proc, so prevent this from messing it up for us.
@@ -13,15 +13,24 @@
density = 1
base_idle_power_usage = 100
base_active_power_usage = 500
max_hardware_size = 3
steel_sheet_cost = 20
/obj/machinery/modular_computer/console/buildable/New()
..()
// User-built consoles start as empty frames.
qdel(tesla_link)
qdel(cpu.network_card)
qdel(cpu.hard_drive)
/obj/machinery/modular_computer/console/New()
..()
battery = null
cpu.network_card = new/datum/computer_hardware/network_card/wired(src)
tesla_link = new/datum/computer_hardware/tesla_link(src)
cpu.battery_module = null
cpu.network_card = new/obj/item/weapon/computer_hardware/network_card/wired(src)
tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src)
tesla_link.enabled = 1
tesla_link.critical = 1 // Consoles don't usually come with cells, and this prevents people from disabling their only power source, as they wouldn't be able to enable it again.
cpu.hard_drive = new/datum/computer_hardware/hard_drive/super(src) // Consoles generally have better HDDs due to lower space limitations
cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(src) // Consoles generally have better HDDs due to lower space limitations
var/area/A = get_area(src)
// Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too.
if(A && console_department)
@@ -35,9 +44,9 @@
if(cpu)
cpu.screen_on = 1
cpu.enabled = 1
install_default_programs()
update_icon()
/*
/obj/machinery/modular_computer/console/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if(istype(W, /obj/item/weapon/cell)) // Power Cell. Try to insert it into the console, if it doesn't have cell installed.
if(!cpu || (stat & (BROKEN|MAINT)))
@@ -51,8 +60,6 @@
W.forceMove(src)
user << "You insert \the [W] into \the [cpu]'s battery slot."
return
if(istype(W, /obj/item/weapon/screwdriver)) // TODO: Screwdriver - begin deconstructing the computer.
return
if(istype(W, /obj/item/weapon/crowbar)) // Crowbar, remove power cell, if it has one.
if(!cpu || (stat & (BROKEN|MAINT)))
user << "\The [cpu] seems to be broken."
@@ -68,4 +75,5 @@
battery_powered = 0
power_change()
return
return ..()
return ..()
*/
@@ -60,6 +60,14 @@
nokeyboard = 1
base_idle_power_usage = 25
base_active_power_usage = 200
max_hardware_size = 2
/obj/machinery/modular_computer/laptop/buildable/New()
..()
// User-built consoles start as empty frames.
qdel(tesla_link)
qdel(cpu.network_card)
qdel(cpu.hard_drive)
// Close the computer. collapsing it into movable item that can't be used.
/obj/machinery/modular_computer/laptop/verb/close_computer()