diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index c7afad3cb6f..8f24ecfc84d 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -7,8 +7,8 @@ use_power = 1 idle_power_usage = 4 active_power_usage = 250 - var/obj/item/weapon/charging = null - var/list/allowed_devices = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/ammo_box/magazine/recharge,/obj/item/weapon/computer_hardware/battery_module,/obj/item/laptop,/obj/item/modular_computer/) + var/obj/item/charging = null + var/list/allowed_devices = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/ammo_box/magazine/recharge,/obj/item/device/modular_computer) var/recharge_coeff = 1 /obj/machinery/recharger/New() @@ -37,9 +37,7 @@ playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) return - var/allowed = 0 - for (var/allowed_type in allowed_devices) - if (istype(G, allowed_type)) allowed = 1 + var/allowed = is_type_in_list(G, allowed_devices) if(allowed) if(anchored) @@ -132,28 +130,10 @@ use_power(200 * recharge_coeff) using_power = 1 - if(istype(charging, /obj/item/laptop)) - var/obj/item/laptop/L = charging - if(L.stored_computer.cpu.battery_module) - var/obj/item/weapon/computer_hardware/battery_module/B = L.stored_computer.cpu.battery_module - if(B.battery) - if(B.battery.charge < B.battery.maxcharge) - B.battery.give(B.battery.chargerate * recharge_coeff) - use_power(200 * recharge_coeff) - using_power = 1 - - if(istype(charging, /obj/item/weapon/computer_hardware/battery_module)) - var/obj/item/weapon/computer_hardware/battery_module/B = charging - if(B.battery) - if(B.battery.charge < B.battery.maxcharge) - B.battery.give(B.battery.chargerate * recharge_coeff) - use_power(200 * recharge_coeff) - using_power = 1 - - if(istype(charging, /obj/item/modular_computer)) - var/obj/item/modular_computer/C = charging + if(istype(charging, /obj/item/device/modular_computer)) + var/obj/item/device/modular_computer/C = charging if(C.battery_module) - var/obj/item/weapon/computer_hardware/battery_module/B = C.battery_module + var/obj/item/weapon/computer_hardware/battery/B = C.battery_module if(B.battery) if(B.battery.charge < B.battery.maxcharge) B.battery.give(B.battery.chargerate * recharge_coeff) diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 38705fb0391..f210c426630 100644 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -99,7 +99,7 @@ Head of Personnel shoes = /obj/item/clothing/shoes/sneakers/brown head = /obj/item/clothing/head/hopcap backpack_contents = list(/obj/item/weapon/storage/box/ids=1,\ - /obj/item/weapon/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced = 1) + /obj/item/weapon/melee/classic_baton/telescopic=1, /obj/item/device/modular_computer/tablet/preset/advanced = 1) /datum/outfit/job/hop/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() diff --git a/code/modules/jobs/job_types/cargo_service.dm b/code/modules/jobs/job_types/cargo_service.dm index adbdf9f6fa0..460d9da5299 100644 --- a/code/modules/jobs/job_types/cargo_service.dm +++ b/code/modules/jobs/job_types/cargo_service.dm @@ -228,4 +228,4 @@ Janitor belt = /obj/item/device/pda/janitor ears = /obj/item/device/radio/headset/headset_srv uniform = /obj/item/clothing/under/rank/janitor - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) + backpack_contents = list(/obj/item/device/modular_computer/tablet/preset/advanced=1) diff --git a/code/modules/jobs/job_types/engineering.dm b/code/modules/jobs/job_types/engineering.dm index 5ec3e6a4ced..eea162ee3ca 100644 --- a/code/modules/jobs/job_types/engineering.dm +++ b/code/modules/jobs/job_types/engineering.dm @@ -36,7 +36,7 @@ Chief Engineer shoes = /obj/item/clothing/shoes/sneakers/brown head = /obj/item/clothing/head/hardhat/white gloves = /obj/item/clothing/gloves/color/black/ce - backpack_contents = list(/obj/item/weapon/melee/classic_baton/telescopic=1,/obj/item/modular_computer/tablet/preset/advanced=1) + backpack_contents = list(/obj/item/weapon/melee/classic_baton/telescopic=1,/obj/item/device/modular_computer/tablet/preset/advanced=1) backpack = /obj/item/weapon/storage/backpack/industrial satchel = /obj/item/weapon/storage/backpack/satchel/eng @@ -89,7 +89,7 @@ Station Engineer dufflebag = /obj/item/weapon/storage/backpack/dufflebag/engineering box = /obj/item/weapon/storage/box/engineer pda_slot = slot_l_store - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) + backpack_contents = list(/obj/item/device/modular_computer/tablet/preset/advanced=1) /* Atmospheric Technician @@ -125,4 +125,4 @@ Atmospheric Technician dufflebag = /obj/item/weapon/storage/backpack/dufflebag/engineering box = /obj/item/weapon/storage/box/engineer pda_slot = slot_l_store - backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1) \ No newline at end of file + backpack_contents = list(/obj/item/device/modular_computer/tablet/preset/advanced=1) \ No newline at end of file diff --git a/code/modules/jobs/job_types/science.dm b/code/modules/jobs/job_types/science.dm index b52f8ac919a..f0ad16f3053 100644 --- a/code/modules/jobs/job_types/science.dm +++ b/code/modules/jobs/job_types/science.dm @@ -38,7 +38,7 @@ Research Director suit = /obj/item/clothing/suit/toggle/labcoat l_hand = /obj/item/weapon/clipboard l_pocket = /obj/item/device/laser_pointer - backpack_contents = list(/obj/item/weapon/melee/classic_baton/telescopic=1,/obj/item/modular_computer/tablet/preset/advanced=1) + backpack_contents = list(/obj/item/weapon/melee/classic_baton/telescopic=1,/obj/item/device/modular_computer/tablet/preset/advanced=1) backpack = /obj/item/weapon/storage/backpack/science satchel = /obj/item/weapon/storage/backpack/satchel/tox diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm index 9cc0170d8de..e0b4be93d0a 100644 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -42,6 +42,8 @@ else use_power = 1 + update_icon() + if(dos_overload) dos_overload = max(0, dos_overload - dos_dissipate) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm new file mode 100644 index 00000000000..71bfc52cfe4 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -0,0 +1,418 @@ +// This is the base type that does all the hardware stuff. +// Other types expand it - tablets use a direct subtypes, and +// consoles and laptops use "procssor" item that is held inside machinery piece +/obj/item/device/modular_computer + name = "modular microcomputer" + desc = "A small portable microcomputer." + + 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 + var/last_power_usage = 0 + var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged + var/last_world_time = "00:00" + var/list/last_header_icons + var/emagged = 0 // Whether the computer is emagged. + + var/base_active_power_usage = 50 // 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 = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops) + + // 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 + // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. + + icon = 'icons/obj/computer.dmi' + icon_state = "laptop-open" + var/icon_state_unpowered = null // Icon state when the computer is turned off. + var/icon_state_powered = null // Icon state when the computer is turned on. + 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 w_class. Tablets/PDAs have 1, laptops 2, consoles 4. + var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer. + + // Damage of the chassis. If the chassis takes too much damage it will break apart. + var/damage = 0 // Current damage level + var/broken_damage = 50 // Damage level at which the computer ceases to operate + var/max_damage = 100 // Damage level at which the computer breaks apart. + + // Important hardware (must be installed for computer to work) + var/obj/item/weapon/computer_hardware/processor_unit/processor_unit // CPU. Without it the computer won't run. Better CPUs can run more programs at once. + 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/battery_module // Power cell connector. Power cell can be recharged. + // OR + var/obj/item/weapon/computer_hardware/recharger/recharger // Recharger. Can be used to recharge power cell or power the PC without it. + + // Optional hardware (improves functionality, but is not critical for computer to work) + 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/card_slot/card_slot // ID Card slot component of this computer. + var/obj/item/weapon/computer_hardware/printer/printer // Printer component of this computer, for your everyday paperwork needs. + var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive // Portable data storage + + var/list/all_components = list() + + var/list/idle_threads = list() // Idle programs on background. They still receive process calls but can't be interacted with. + var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks. + + + +/obj/item/device/modular_computer/New() + START_PROCESSING(SSobj, src) + update_icon() + if(!physical) + physical = src + ..() + +/obj/item/device/modular_computer/Destroy() + kill_program(forced = TRUE) + STOP_PROCESSING(SSobj, src) + for(var/H in all_components) + qdel(H) + return ..() + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/device/modular_computer/verb/eject_id() + set name = "Eject ID" + set category = "Object" + set src in view(1) + + if(issilicon(usr)) + return + + if (usr.canUseTopic(src)) + proc_eject_id(usr) + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/device/modular_computer/verb/eject_disk() + set name = "Eject Data Disk" + set category = "Object" + set src in view(1) + + if(issilicon(usr)) + return + + if (usr.canUseTopic(src)) + proc_eject_disk(usr) + +/obj/item/device/modular_computer/proc/proc_eject_id(mob/user, slot) + if(!user) + user = usr + + if(!card_slot) + user << "\The [src] does not have an ID card slot!" + return + + card_slot.try_eject(slot, user) + +/obj/item/device/modular_computer/proc/proc_eject_disk(mob/user) + if(!user) + user = usr + + if(!portable_drive) + user << "There is no data disk in \the [src]!" + return + + var/obj/item/I = portable_drive + if(uninstall_component(portable_drive, user)) + I.verb_pickup() + +/obj/item/device/modular_computer/AltClick(mob/user) + ..() + if(issilicon(user)) + return + + if(user.canUseTopic(src)) + if(portable_drive) + proc_eject_disk(user) + else if(card_slot) + proc_eject_id(user) + + +// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. +/obj/item/device/modular_computer/GetAccess() + if(card_slot) + return card_slot.GetAccess() + return ..() + +/obj/item/device/modular_computer/GetID() + if(card_slot) + return card_slot.GetID() + return ..() + +/obj/item/device/modular_computer/MouseDrop(obj/over_object, src_location, over_location) + var/mob/M = usr + if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src)) + return attack_self(M) + return + +/obj/item/device/modular_computer/attack_ghost(mob/dead/observer/user) + if(enabled) + ui_interact(user) + else if(IsAdminGhost(user)) + var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") + if(response == "Yes") + turn_on(user) + +/obj/item/device/modular_computer/emag_act(mob/user) + if(emagged) + user << "\The [src] was already emagged." + return 0 + else + emagged = 1 + user << "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message." + return 1 + +/obj/item/device/modular_computer/examine(mob/user) + ..() + if(damage > broken_damage) + user << "It is heavily damaged!" + else if(damage) + user << "It is damaged." + +/obj/item/device/modular_computer/update_icon() + cut_overlays() + if(!enabled) + icon_state = icon_state_unpowered + else + icon_state = icon_state_powered + if(active_program) + add_overlay(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu) + else + add_overlay(icon_state_menu) + + if(damage > broken_damage) + add_overlay("bsod") + add_overlay("broken") + + +// On-click handling. Turns on the computer if it's off and opens the GUI. +/obj/item/device/modular_computer/attack_self(mob/user) + if(enabled) + ui_interact(user) + else + turn_on(user) + +/obj/item/device/modular_computer/proc/turn_on(mob/user) + var/issynth = issilicon(user) // Robots and AIs get different activation messages. + if(damage > broken_damage) + if(issynth) + user << "You send an activation signal to \the [src], but it responds with an error code. It must be damaged." + else + user << "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again." + return + + // If we have a recharger, enable it automatically. Lets computer without a battery work. + if(recharger) + recharger.enabled = 1 + + if(processor_unit && use_power()) // use_power() checks if the PC is powered + if(issynth) + user << "You send an activation signal to \the [src], turning it on." + else + user << "You press the power button and start up \the [src]." + enabled = 1 + update_icon() + ui_interact(user) + else // Unpowered + if(issynth) + user << "You send an activation signal to \the [src] but it does not respond." + else + user << "You press the power button but \the [src] does not respond." + +// Process currently calls handle_power(), may be expanded in future if more things are added. +/obj/item/device/modular_computer/process() + if(!enabled) // The computer is turned off + last_power_usage = 0 + return 0 + + if(damage > broken_damage) + shutdown_computer() + return 0 + + if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) + active_program.event_networkfailure(0) // Active program requires NTNet to run but we've just lost connection. Crash. + + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) + P.event_networkfailure(1) + + if(active_program) + if(active_program.program_state != PROGRAM_STATE_KILLED) + active_program.process_tick() + active_program.ntnet_status = get_ntnet_status() + else + active_program = null + + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(P.program_state != PROGRAM_STATE_KILLED) + P.process_tick() + P.ntnet_status = get_ntnet_status() + else + idle_threads.Remove(P) + + handle_power() // Handles all computer power interaction + //check_update_ui_need() + +// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" +/obj/item/device/modular_computer/proc/get_header_data() + var/list/data = list() + + if(battery_module && battery_module.battery) + 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) + data["PC_batteryicon"] = "batt_80.gif" + if(40 to 60) + data["PC_batteryicon"] = "batt_60.gif" + if(20 to 40) + data["PC_batteryicon"] = "batt_40.gif" + if(5 to 20) + data["PC_batteryicon"] = "batt_20.gif" + else + data["PC_batteryicon"] = "batt_5.gif" + 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_module ? 1 : 0 + + if(recharger && recharger.enabled && recharger.check_functionality() && recharger.use_power(0)) + data["PC_apclinkicon"] = "charging.gif" + + switch(get_ntnet_status()) + if(0) + data["PC_ntneticon"] = "sig_none.gif" + if(1) + data["PC_ntneticon"] = "sig_low.gif" + if(2) + data["PC_ntneticon"] = "sig_high.gif" + if(3) + data["PC_ntneticon"] = "sig_lan.gif" + + if(idle_threads.len) + var/list/program_headers = list() + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(!P.ui_header) + continue + program_headers.Add(list(list( + "icon" = P.ui_header + ))) + + data["PC_programheaders"] = program_headers + + data["PC_stationtime"] = worldtime2text() + data["PC_hasheader"] = 1 + data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen + return data + +// Relays kill program request to currently active program. Use this to quit current program. +/obj/item/device/modular_computer/proc/kill_program(forced = FALSE) + if(active_program) + active_program.kill_program(forced) + active_program = null + var/mob/user = usr + if(user && istype(user)) + ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + update_icon() + +// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) +/obj/item/device/modular_computer/proc/get_ntnet_status(specific_action = 0) + if(network_card) + return network_card.get_signal(specific_action) + else + return 0 + +/obj/item/device/modular_computer/proc/add_log(text) + if(!get_ntnet_status()) + return FALSE + return ntnet_global.add_log(text, network_card) + +/obj/item/device/modular_computer/proc/shutdown_computer(loud = 1) + kill_program(forced = TRUE) + for(var/datum/computer_file/program/P in idle_threads) + P.kill_program(forced = TRUE) + idle_threads.Remove(P) + if(loud) + physical.visible_message("\The [src] shuts down.") + enabled = 0 + update_icon() + + +/obj/item/device/modular_computer/attackby(obj/item/weapon/W as obj, mob/user as mob) + // Insert items into the components + for(var/h in all_components) + var/obj/item/weapon/computer_hardware/H = h + if(H.try_insert(W, user)) + return + + // Insert new hardware + if(istype(W, /obj/item/weapon/computer_hardware)) + if(install_component(W, user)) + return + + if(istype(W, /obj/item/weapon/wrench)) + if(all_components.len) + user << "Remove all components from \the [src] before disassembling it." + return + new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost ) + physical.visible_message("\The [src] has been disassembled by [user].") + relay_qdel() + qdel(src) + return + + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(!WT.isOn()) + user << "\The [W] is off." + return + + if(!damage) + user << "\The [src] does not require repairs." + return + + user << "You begin repairing damage to \the [src]..." + if(WT.remove_fuel(round(damage/75)) && do_after(usr, damage/10)) + damage = 0 + user << "You repair \the [src]." + return + + if(istype(W, /obj/item/weapon/screwdriver)) + 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(H, user) + return + + ..() + +// Used by processor to relay qdel() to machinery type. +/obj/item/device/modular_computer/proc/relay_qdel() + return + +// Perform adjacency checks on our physical counterpart, if any. +/obj/item/device/modular_computer/Adjacent(atom/neighbor) + if(physical && physical != src) + return physical.Adjacent(neighbor) + return ..() diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm new file mode 100644 index 00000000000..3a5b68579e1 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_components.dm @@ -0,0 +1,117 @@ +/obj/item/device/modular_computer/proc/can_install_component(obj/item/weapon/computer_hardware/H, mob/living/user = null) + if(!H.can_install(src, user)) + return FALSE + + if(H.w_class > max_hardware_size) + user << "This component is too large for \the [src]!" + return FALSE + + if(istype(H, /obj/item/weapon/computer_hardware/hard_drive/portable) && portable_drive) + user << "This computer's FDD is already occupied by \the [portable_drive]." + return FALSE + + else if(istype(H, /obj/item/weapon/computer_hardware/processor_unit) && processor_unit) + user << "This computer's CPU slot is already occupied by \the [processor_unit]." + return FALSE + else if(istype(H, /obj/item/weapon/computer_hardware/hard_drive) && hard_drive) + user << "This computer's data port is already occupied by \the [hard_drive]." + return FALSE + + else if(istype(H, /obj/item/weapon/computer_hardware/battery) && battery_module) + user << "This computer's power converter slot is already occupied by \the [battery_module]." + return FALSE + else if(istype(H, /obj/item/weapon/computer_hardware/recharger) && recharger) + user << "This computer's recharger slot is already occupied by \the [recharger]." + return FALSE + + else if(istype(H, /obj/item/weapon/computer_hardware/card_slot) && card_slot) + user << "This computer's card reader slot is already occupied by \the [card_slot]." + return FALSE + else if(istype(H, /obj/item/weapon/computer_hardware/network_card) && network_card) + user << "This computer's network card is already occupied by \the [network_card]." + return FALSE + else if(istype(H, /obj/item/weapon/computer_hardware/printer) && printer) + user << "This computer's printer slot is already occupied by \the [printer]." + return FALSE + return TRUE + + +// Installs component. +/obj/item/device/modular_computer/proc/install_component(obj/item/weapon/computer_hardware/H, mob/living/user = null) + if(!can_install_component(H, user)) + return FALSE + + if(user && !user.unEquip(H)) + return FALSE + + if(istype(H, /obj/item/weapon/computer_hardware/hard_drive/portable)) + portable_drive = H + + else if(istype(H, /obj/item/weapon/computer_hardware/processor_unit)) + processor_unit = H + else if(istype(H, /obj/item/weapon/computer_hardware/hard_drive)) + hard_drive = H + + else if(istype(H, /obj/item/weapon/computer_hardware/battery)) + battery_module = H + else if(istype(H, /obj/item/weapon/computer_hardware/recharger)) + recharger = H + + else if(istype(H, /obj/item/weapon/computer_hardware/card_slot)) + card_slot = H + else if(istype(H, /obj/item/weapon/computer_hardware/network_card)) + network_card = H + else if(istype(H, /obj/item/weapon/computer_hardware/printer)) + printer = H + + user << "You install \the [H] into \the [src]." + H.forceMove(src) + H.holder = src + all_components |= H + H.on_install(src, user) + return TRUE + + +// Uninstalls component. +/obj/item/device/modular_computer/proc/uninstall_component(obj/item/weapon/computer_hardware/H, mob/living/user = null) + if(H.holder != src) // Not our component at all. + return FALSE + + if(processor_unit == H) + processor_unit = null + if(hard_drive == H) + hard_drive = null + + if(battery_module == H) + battery_module = null + if(recharger == H) + recharger = null + + if(card_slot == H) + card_slot = null + if(network_card == H) + network_card = null + if(printer == H) + printer = null + if(portable_drive == H) + portable_drive = null + + user << "You remove \the [H] from \the [src]." + + H.forceMove(get_turf(src)) + H.holder = null + all_components -= H + H.on_remove(src, user) + if(enabled && (!processor_unit || !hard_drive || !use_power())) + shutdown_computer() + update_icon() + return TRUE + + +// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null +/obj/item/device/modular_computer/proc/find_hardware_by_name(name) + for(var/i in all_components) + var/obj/O = i + if(O.name == name) + return O + return null diff --git a/code/modules/modular_computers/computers/item/computer_damage.dm b/code/modules/modular_computers/computers/item/computer_damage.dm new file mode 100644 index 00000000000..8214840bc2b --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_damage.dm @@ -0,0 +1,49 @@ +/obj/item/device/modular_computer/proc/take_damage(amount, component_probability, damage_casing = 1, randomize = 1) + if(randomize) + // 75%-125%, rand() works with integers, apparently. + amount *= (rand(75, 125) / 100.0) + amount = round(amount) + if(damage_casing) + damage += amount + damage = max(0,min(max_damage,damage)) + + if(component_probability) + for(var/I in all_components) + var/obj/item/weapon/computer_hardware/H = I + if(prob(component_probability)) + H.take_damage(round(amount / 2)) + + if(damage >= max_damage) + break_apart() + +// Stronger explosions cause serious damage to internal components +// Minor explosions are mostly mitigitated by casing. +/obj/item/device/modular_computer/ex_act(severity) + take_damage(rand(100,200)/severity, component_probability = 30/severity) + +// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components +/obj/item/device/modular_computer/emp_act(severity) + take_damage(rand(100,200)/severity, component_probability = 50/severity, damage_casing = 0) + +// "Stun" weapons can cause minor damage to components (short-circuits?) +// "Burn" damage is equally strong against internal components and exterior casing +// "Brute" damage mostly damages the casing. +/obj/item/device/modular_computer/bullet_act(obj/item/projectile/Proj) + switch(Proj.damage_type) + if(BRUTE) + take_damage(Proj.damage, component_probability = Proj.damage/2) + if(BURN) + take_damage(Proj.damage, component_probability = Proj.damage/1.5) + +/obj/item/device/modular_computer/proc/break_apart() + physical.visible_message("\The [src] breaks apart!") + var/turf/newloc = get_turf(src) + new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2)) + for(var/C in all_components) + var/obj/item/weapon/computer_hardware/H = C + uninstall_component(H) + H.forceMove(newloc) + if(prob(25)) + H.take_damage(rand(10,30)) + relay_qdel() + qdel() diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm new file mode 100644 index 00000000000..f6196477000 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -0,0 +1,55 @@ +// Tries to draw power from charger or, if no operational charger is present, from power cell. +/obj/item/device/modular_computer/proc/use_power(amount = 0) + if(check_power_override()) + return TRUE + + if(recharger && recharger.check_functionality()) + if(recharger.use_power(amount)) + return TRUE + + if(battery_module && battery_module.battery && battery_module.battery.charge) + var/obj/item/weapon/stock_parts/cell/cell = battery_module.battery + if(cell.use(amount * CELLRATE)) + return TRUE + else // Discharge the cell anyway. + cell.use(min(amount*CELLRATE, cell.charge)) + return FALSE + return FALSE + +/obj/item/device/modular_computer/proc/give_power(amount) + if(battery_module && battery_module.battery) + return battery_module.battery.give(amount) + return 0 + + +// Used in following function to reduce copypaste +/obj/item/device/modular_computer/proc/power_failure() + if(enabled) // Shut down the computer + if(active_program) + active_program.event_powerfailure(0) + for(var/I in idle_threads) + var/datum/computer_file/program/PRG = I + PRG.event_powerfailure(1) + shutdown_computer(0) + +// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged +/obj/item/device/modular_computer/proc/handle_power() + if(recharger) + recharger.process() + + var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage + + for(var/obj/item/weapon/computer_hardware/H in all_components) + if(H.enabled) + power_usage += H.power_usage + + if(use_power(power_usage)) + last_power_usage = power_usage + return TRUE + else + power_failure() + return FALSE + +// Used by child types if they have other power source than battery or recharger +/obj/item/device/modular_computer/proc/check_power_override() + return FALSE diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm new file mode 100644 index 00000000000..a89e84b31f4 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -0,0 +1,132 @@ +// Operates TGUI +/obj/item/device/modular_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) + if(!enabled) + if(ui) + ui.close() + return 0 + if(!use_power()) + if(ui) + ui.close() + return 0 + + // Robots don't really need to see the screen, their wireless connection works as long as computer is on. + if(!screen_on && !issilicon(user)) + if(ui) + ui.close() + return 0 + + // If we have an active program switch to it now. + if(active_program) + if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now. + ui.close() + active_program.ui_interact(user) + return + + // We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it. + // This screen simply lists available programs and user may select them. + if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len) + physical.visible_message("\The [src] beeps three times, it's screen displaying \"DISK ERROR\" warning.") + return // No HDD, No HDD files list or no stored files. Something is very broken. + + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if (!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + + ui = new(user, src, ui_key, "computer_main", "NTOS Main menu", 400, 500, master_ui, state) + ui.open() + ui.set_autoupdate(state = 1) + + +/obj/item/device/modular_computer/ui_data(mob/user) + var/list/data = get_header_data() + data["programs"] = list() + for(var/datum/computer_file/program/P in hard_drive.stored_files) + var/running = 0 + if(P in idle_threads) + running = 1 + + data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running)) + + return data + + +// Handles user's GUI input +/obj/item/device/modular_computer/ui_act(action, params) + if(..()) + return + switch(action) + if("PC_exit") + kill_program() + return 1 + if("PC_shutdown") + shutdown_computer() + return 1 + if("PC_minimize") + var/mob/user = usr + if(!active_program || !processor_unit) + return + + idle_threads.Add(active_program) + active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs + + active_program = null + update_icon() + if(user && istype(user)) + ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + + if("PC_killprogram") + var/prog = params["name"] + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) + + if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) + return + + P.kill_program(forced = TRUE) + user << "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed." + + if("PC_runprogram") + var/prog = params["name"] + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) + + if(!P || !istype(P)) // Program not found or it's not executable program. + user << "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning." + return + + P.computer = src + + if(!P.is_supported_by_hardware(hardware_flag, 1, user)) + return + + // The program is already running. Resume it. + if(P in idle_threads) + P.program_state = PROGRAM_STATE_ACTIVE + active_program = P + idle_threads.Remove(P) + update_icon() + return + + if(idle_threads.len >= processor_unit.max_idle_programs+1) + user << "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error." + return + + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. + user << "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning." + return + if(P.run_program(user)) + active_program = P + update_icon() + return 1 + else + return + +/obj/item/device/modular_computer/ui_host() + if(physical) + return physical + return src diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm new file mode 100644 index 00000000000..51c91a595ae --- /dev/null +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -0,0 +1,106 @@ +/obj/item/device/modular_computer/laptop + name = "laptop" + desc = "A portable laptop computer." + + icon = 'icons/obj/modular_laptop.dmi' + icon_state = "laptop-closed" + icon_state_powered = "laptop" + icon_state_unpowered = "laptop-off" + icon_state_menu = "menu" + + hardware_flag = PROGRAM_LAPTOP + max_hardware_size = 2 + w_class = 3 + + flags = HANDSLOW // No running around with open laptops in hands. + + screen_on = 0 // Starts closed + var/start_open = 1 // unless this var is set to 1 + var/icon_state_closed = "laptop-closed" + var/w_class_open = 4 + var/slowdown_open = 1 + +/obj/item/device/modular_computer/laptop/New() + ..() + if(start_open && !screen_on) + toggle_open() + +/obj/item/device/modular_computer/laptop/update_icon() + if(screen_on) + ..() + else + cut_overlays() + icon_state = icon_state_closed + +/obj/item/device/modular_computer/laptop/attack_self(mob/user) + if(!screen_on) + try_toggle_open(user) + else + return ..() + +/obj/item/device/modular_computer/laptop/verb/open_computer() + set name = "Toggle Open" + set category = "Object" + set src in view(1) + + try_toggle_open(usr) + +/obj/item/device/modular_computer/laptop/MouseDrop(obj/over_object, src_location, over_location) + if(over_object == usr || over_object == src) + try_toggle_open(usr) + else if(istype(over_object, /obj/screen/inventory/hand)) + var/obj/screen/inventory/hand/H = over_object + var/mob/M = usr + + if(!M.restrained() && !M.stat) + if(!isturf(loc) || !Adjacent(M)) + return + + switch(H.slot_id) + if(slot_r_hand) + M.put_in_r_hand(src) + if(slot_l_hand) + M.put_in_l_hand(src) + +/obj/item/device/modular_computer/laptop/attack_hand(mob/user) + if(screen_on && isturf(loc)) + return attack_self(user) + + return ..() + + +/obj/item/device/modular_computer/laptop/proc/try_toggle_open(mob/living/user) + if(issilicon(user)) + return + if(!isturf(loc) && !ismob(loc)) // No opening it in backpack. + return + if(!user.canUseTopic(src)) + return + + toggle_open(user) + + +/obj/item/device/modular_computer/laptop/AltClick(mob/user) + if(screen_on) // Close it. + try_toggle_open(user) + else + return ..() + +/obj/item/device/modular_computer/laptop/proc/toggle_open(mob/living/user=null) + if(screen_on) + user << "You close \the [src]." + slowdown = initial(slowdown) + w_class = initial(w_class) + else + user << "You open \the [src]." + slowdown = slowdown_open + w_class = w_class_open + + screen_on = !screen_on + update_icon() + + + +// Laptop frame, starts empty and closed. +/obj/item/device/modular_computer/laptop/buildable + start_open = 0 diff --git a/code/modules/modular_computers/computers/item/modular_computer.dm b/code/modules/modular_computers/computers/item/modular_computer.dm deleted file mode 100644 index 6314cb69913..00000000000 --- a/code/modules/modular_computers/computers/item/modular_computer.dm +++ /dev/null @@ -1,704 +0,0 @@ -// This is the base type that does all the hardware stuff. -// Other types expand it - tablets use a direct subtypes, and -// consoles and laptops use "procssor" item that is held inside machinery piece -/obj/item/modular_computer - name = "Modular Microcomputer" - desc = "A small portable microcomputer." - - 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 - var/last_power_usage = 0 - var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged - var/last_world_time = "00:00" - var/list/last_header_icons - var/computer_emagged = 0 // Whether the computer is emagged. - - var/base_active_power_usage = 50 // 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 = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops) - - // 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 - // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. - - icon = 'icons/obj/computer.dmi' - 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. - - // Damage of the chassis. If the chassis takes too much damage it will break apart. - var/damage = 0 // Current damage level - var/broken_damage = 50 // Damage level at which the computer ceases to operate - var/max_damage = 100 // Damage level at which the computer breaks apart. - - // Important hardware (must be installed for computer to work) - var/obj/item/weapon/computer_hardware/processor_unit/processor_unit // CPU. Without it the computer won't run. Better CPUs can run more programs at once. - 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/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/card_slot/card_slot2 // second slot. popular demand requested the hop need to insert 2 ids. - 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 - - var/list/idle_threads = list() // Idle programs on background. They still receive process calls but can't be interacted with. - var/obj/physical = null - - - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/verb/eject_id() - set name = "Eject ID" - set category = "Object" - set src in view(1) - - if(usr.incapacitated() || !istype(usr, /mob/living)) - usr << "You can't do that." - return - - if(!Adjacent(usr)) - usr << "You can't reach it." - return - - proc_eject_id(usr) - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/verb/eject_usb() - set name = "Eject Portable Device" - set category = "Object" - set src in view(1) - - if(usr.incapacitated() || !istype(usr, /mob/living)) - usr << "You can't do that." - return - - if(!Adjacent(usr)) - usr << "You can't reach it." - return - - proc_eject_usb(usr) - -/obj/item/modular_computer/proc/proc_eject_id(mob/user, slot) - if(!user) - user = usr - - if(!card_slot) - user << "\The [src] does not have an ID card slot" - return - - if(!card_slot.stored_card && !card_slot.stored_card2) - user << "There is no card in \the [src]" - return - - if(active_program) - active_program.event_idremoved(0, slot) - - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - P.event_idremoved(1, slot) - if(card_slot.stored_card && (!slot || slot == 1)) - card_slot.stored_card.forceMove(get_turf(src)) - card_slot.stored_card = null - if(card_slot.stored_card2 && (!slot || slot == 2)) - card_slot.stored_card2.forceMove(get_turf(src)) - card_slot.stored_card2 = null - user << "You remove the card[card_slot.stored_card && card_slot.stored_card2 && !slot ? "s" : ""] from \the [src]" - -/obj/item/modular_computer/proc/proc_eject_usb(mob/user) - if(!user) - user = usr - - if(!portable_drive) - user << "There is no portable device connected to \the [src]." - return - - uninstall_component(user, portable_drive) - -/obj/item/modular_computer/attack_ghost(mob/dead/observer/user) - if(enabled) - ui_interact(user) - else if(IsAdminGhost(user)) - var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") - if(response == "Yes") - turn_on(user) - -/obj/item/modular_computer/emag_act(mob/user) - if(computer_emagged) - user << "\The [src] was already emagged." - return 0 - else - computer_emagged = 1 - user << "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message." - return 1 - -/obj/item/modular_computer/examine(mob/user) - ..() - if(damage > broken_damage) - user << "It is heavily damaged!" - else if(damage) - user << "It is damaged." - -/obj/item/modular_computer/New() - START_PROCESSING(SSmachine, src) - update_icon() - if(!physical) - physical = src - ..() - -/obj/item/modular_computer/Destroy() - kill_program(1) - STOP_PROCESSING(SSmachine, src) - for(var/H in get_all_components()) - var/obj/item/weapon/computer_hardware/CH = H - uninstall_component(null, CH) - qdel(CH) - return ..() - -/obj/item/modular_computer/update_icon() - icon_state = icon_state_unpowered - cut_overlays() - if(!enabled) - return - if(active_program) - add_overlay(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu) - else - add_overlay(icon_state_menu) - -// Used by child types if they have other power source than battery -/obj/item/modular_computer/proc/check_power_override() - return 0 - -// Operates NanoUI -/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) - - - if(!screen_on || !enabled) - if(ui) - ui.close() - return 0 - if((!battery_module || !battery_module.battery.charge) && !check_power_override()) - if(ui) - ui.close() - return 0 - - // If we have an active program switch to it now. - if(active_program) - if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now. - ui.close() - active_program.ui_interact(user) - return - - // We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it. - // This screen simply lists available programs and user may select them. - if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len) - physical.visible_message("\The [src] beeps three times, it's screen displaying \"DISK ERROR\" warning.") - return // No HDD, No HDD files list or no stored files. Something is very broken. - - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if (!ui) - - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - - ui = new(user, src, ui_key, "computer_main", "NTOS Main menu", 400, 500, master_ui, state) - ui.open() - ui.set_autoupdate(state = 1) - - -/obj/item/modular_computer/ui_data(mob/user) - - var/list/data = get_header_data() - data["programs"] = list() - for(var/datum/computer_file/program/P in hard_drive.stored_files) - var/running = 0 - if(P in idle_threads) - running = 1 - - data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running)) - - return data - - -// On-click handling. Turns on the computer if it's off and opens the GUI. -/obj/item/modular_computer/attack_self(mob/user) - if(enabled) - ui_interact(user) - else - turn_on(user) - -/obj/item/modular_computer/proc/break_apart() - physical.visible_message("\The [src] breaks apart!") - var/turf/newloc = get_turf(src) - new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2)) - for(var/C in get_all_components()) - var/obj/item/weapon/computer_hardware/H = C - uninstall_component(null, H) - H.forceMove(newloc) - if(prob(25)) - H.take_damage(rand(10,30)) - relay_qdel() - qdel() - -/obj/item/modular_computer/proc/turn_on(mob/user) - var/issynth = issilicon(user) // Robots and AIs get different activation messages. - if(damage > broken_damage) - if(issynth) - user << "You send an activation signal to \the [src], but it responds with an error code. It must be damaged." - else - user << "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again." - return - if(processor_unit && ((battery_module && battery_module.battery.charge && battery_module.check_functionality()) || check_power_override())) // Battery-run and charged or non-battery but powered by APC. - if(issynth) - user << "You send an activation signal to \the [src], turning it on" - else - user << "You press the power button and start up \the [src]" - enabled = 1 - update_icon() - ui_interact(user) - else // Unpowered - if(issynth) - user << "You send an activation signal to \the [src] but it does not respond" - else - user << "You press the power button but \the [src] does not respond" - -// Process currently calls handle_power(), may be expanded in future if more things are added. -/obj/item/modular_computer/process() - if(!enabled) // The computer is turned off - last_power_usage = 0 - return 0 - - if(damage > broken_damage) - shutdown_computer() - return 0 - - if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) // Active program requires NTNet to run but we've just lost connection. Crash. - active_program.event_networkfailure(0) - - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) - P.event_networkfailure(1) - - if(active_program) - if(active_program.program_state != PROGRAM_STATE_KILLED) - active_program.process_tick() - active_program.ntnet_status = get_ntnet_status() - active_program.computer_emagged = computer_emagged - else - active_program = null - - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(P.program_state != PROGRAM_STATE_KILLED) - P.process_tick() - P.ntnet_status = get_ntnet_status() - P.computer_emagged = computer_emagged - else - idle_threads.Remove(P) - - handle_power() // Handles all computer power interaction - //check_update_ui_need() - -// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" -/obj/item/modular_computer/proc/get_header_data() - var/list/data = list() - - 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) - data["PC_batteryicon"] = "batt_80.gif" - if(40 to 60) - data["PC_batteryicon"] = "batt_60.gif" - if(20 to 40) - data["PC_batteryicon"] = "batt_40.gif" - if(5 to 20) - data["PC_batteryicon"] = "batt_20.gif" - else - data["PC_batteryicon"] = "batt_5.gif" - 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_module ? 1 : 0 - - switch(get_ntnet_status()) - if(0) - data["PC_ntneticon"] = "sig_none.gif" - if(1) - data["PC_ntneticon"] = "sig_low.gif" - if(2) - data["PC_ntneticon"] = "sig_high.gif" - if(3) - data["PC_ntneticon"] = "sig_lan.gif" - - if(idle_threads.len) - var/list/program_headers = list() - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(!P.ui_header) - continue - program_headers.Add(list(list( - "icon" = P.ui_header - ))) - - data["PC_programheaders"] = program_headers - - data["PC_stationtime"] = worldtime2text() - data["PC_hasheader"] = 1 - data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen - return data - -// Relays kill program request to currently active program. Use this to quit current program. -/obj/item/modular_computer/proc/kill_program(forced = 0) - if(active_program) - active_program.kill_program(forced) - active_program = null - var/mob/user = usr - if(user && istype(user)) - ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. - update_icon() - -// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) -/obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0) - if(network_card) - return network_card.get_signal(specific_action) - else - return 0 - -/obj/item/modular_computer/proc/add_log(text) - if(!get_ntnet_status()) - return 0 - return ntnet_global.add_log(text, network_card) - -/obj/item/modular_computer/proc/shutdown_computer(loud = 1) - kill_program(1) - for(var/datum/computer_file/program/P in idle_threads) - P.kill_program(1) - idle_threads.Remove(P) - if(loud) - physical.visible_message("\The [src] shuts down.") - enabled = 0 - update_icon() - return - -// Handles user's GUI input -/obj/item/modular_computer/ui_act(action, params) - if(..()) - return - switch(action) - if("PC_exit") - kill_program() - return 1 - if("PC_shutdown") - shutdown_computer() - return 1 - if("PC_minimize") - var/mob/user = usr - if(!active_program || !processor_unit) - return - - idle_threads.Add(active_program) - active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs - - active_program = null - update_icon() - if(user && istype(user)) - ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. - - if("PC_killprogram") - var/prog = params["name"] - var/datum/computer_file/program/P = null - var/mob/user = usr - if(hard_drive) - P = hard_drive.find_file_by_name(prog) - - if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) - return - - P.kill_program(1) - user << "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed." - - if("PC_runprogram") - var/prog = params["name"] - var/datum/computer_file/program/P = null - var/mob/user = usr - if(hard_drive) - P = hard_drive.find_file_by_name(prog) - - if(!P || !istype(P)) // Program not found or it's not executable program. - user << "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning." - return - - P.computer = src - - if(!P.is_supported_by_hardware(hardware_flag, 1, user)) - return - - // The program is already running. Resume it. - if(P in idle_threads) - P.program_state = PROGRAM_STATE_ACTIVE - active_program = P - idle_threads.Remove(P) - update_icon() - return - - if(idle_threads.len >= processor_unit.max_idle_programs+1) - user << "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error" - return - - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. - user << "\The [src]'s screen shows \"NETWORK ERROR - Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning." - return - P.computer_emagged = computer_emagged - if(P.run_program(user)) - active_program = P - update_icon() - return 1 - else - return - -// Used in following function to reduce copypaste -/obj/item/modular_computer/proc/power_failure(malfunction = 0) - if(enabled) // Shut down the computer - physical.visible_message("\The [src]'s screen flickers \"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\" warning as it shuts down unexpectedly.") - if(active_program) - active_program.event_powerfailure(0) - for(var/I in idle_threads) - var/datum/computer_file/program/PRG = I - PRG.event_powerfailure(1) - shutdown_computer(0) - -// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged -/obj/item/modular_computer/proc/handle_power() - if(!battery_module || battery_module.battery.charge <= 0) // Battery-run but battery is depleted. - power_failure() - return 0 - - var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage - - for(var/obj/item/weapon/computer_hardware/H in get_all_components()) - if(H.enabled) - power_usage += H.power_usage - - if(battery_module) - if(!battery_module.check_functionality()) - power_failure(1) - return - if(!battery_module.battery.use(power_usage * CELLRATE)) - battery_module.battery.charge = 0 - power_failure() - return 0 - - last_power_usage = power_usage - -/obj/item/modular_computer/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/card/id)) // ID Card, try to insert it. - var/obj/item/weapon/card/id/I = W - if(!card_slot) - user << "You try to insert \the [I] into \the [src], but it does not have an ID card slot installed." - return - - if(card_slot.stored_card && card_slot.stored_card2) - user << "You try to insert \the [I] into \the [src], but it's ID card slots are occupied." - return - if(!user.drop_item(I)) - return - if(!card_slot.stored_card) - card_slot.stored_card = I - else - card_slot.stored_card2 = I - I.forceMove(src) -// update_uis() - user << "You insert \the [I] into \the [src]." - return - if(istype(W, /obj/item/weapon/paper)) - if(!nano_printer) - return - nano_printer.attackby(W, user) - if(istype(W, /obj/item/weapon/computer_hardware)) - var/obj/item/weapon/computer_hardware/C = W - if(C.hardware_size <= max_hardware_size) - C.try_install_component(user, src) - 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/sheet/metal( get_turf(src.loc), steel_sheet_cost ) - physical.visible_message("\The [src] has been disassembled by [user].") - relay_qdel() - qdel(src) - return - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(!WT.isOn()) - user << "\The [W] is off." - return - - if(!damage) - user << "\The [src] does not require repairs." - return - - user << "You begin repairing damage to \the [src]..." - if(WT.remove_fuel(round(damage/75)) && do_after(usr, damage/10)) - damage = 0 - user << "You repair \the [src]." - return - - 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 - - ..() - -// Used by processor to relay qdel() to machinery type. -/obj/item/modular_computer/proc/relay_qdel() - return - -// Uninstalls component. Found and Critical vars may be passed by parent types, if they have additional hardware. -/obj/item/modular_computer/proc/uninstall_component(mob/living/user, obj/item/weapon/computer_hardware/H, found = 0, 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(processor_unit == H) - processor_unit = null - found = 1 - critical = 1 - if(found) - if(user) - user << "You remove \the [H] from \the [src]." - H.forceMove(get_turf(src)) - H.holder2 = null - if(critical && enabled) - if(user) - user << "\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." - shutdown_computer() - 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(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)) - return network_card - if(nano_printer && (nano_printer.name == name)) - return nano_printer - if(card_slot && (card_slot.name == name)) - return card_slot - if(battery_module && (battery_module.name == name)) - return battery_module - if(processor_unit && (processor_unit.name == name)) - return processor_unit - 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) - if(processor_unit) - all_components.Add(processor_unit) - return all_components - -/obj/item/modular_computer/proc/take_damage(amount, component_probability, damage_casing = 1, randomize = 1) - if(randomize) - // 75%-125%, rand() works with integers, apparently. - amount *= (rand(75, 125) / 100.0) - amount = round(amount) - if(damage_casing) - damage += amount - damage = max(0,min(max_damage,damage)) - - if(component_probability) - for(var/I in get_all_components()) - var/obj/item/weapon/computer_hardware/H = I - if(prob(component_probability)) - H.take_damage(round(amount / 2)) - - if(damage >= max_damage) - break_apart() - -// Stronger explosions cause serious damage to internal components -// Minor explosions are mostly mitigitated by casing. -/obj/item/modular_computer/ex_act(severity) - take_damage(rand(100,200) / severity, 30 / severity) - -// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components -/obj/item/modular_computer/emp_act(severity) - take_damage(rand(100,200) / severity, 50 / severity, 0) - -// "Stun" weapons can cause minor damage to components (short-circuits?) -// "Burn" damage is equally strong against internal components and exterior casing -// "Brute" damage mostly damages the casing. -/obj/item/modular_computer/bullet_act(obj/item/projectile/Proj) - switch(Proj.damage_type) - if(BRUTE) - take_damage(Proj.damage, Proj.damage / 2) - if(BURN) - take_damage(Proj.damage, Proj.damage / 1.5) - -/obj/item/modular_computer/ui_host() - if(physical) - return physical - else - return src \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index f9148b66596..65712546ef6 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -1,5 +1,5 @@ // Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code. -/obj/item/modular_computer/processor +/obj/item/device/modular_computer/processor name = "processing unit" desc = "You shouldn't see this. If you do, report it." icon = null @@ -10,35 +10,17 @@ var/obj/machinery/modular_computer/machinery_computer = null -/obj/item/modular_computer/processor/Destroy() +/obj/item/device/modular_computer/processor/Destroy() . = ..() if(machinery_computer && (machinery_computer.cpu == src)) machinery_computer.cpu = null machinery_computer = null -// Due to how processes work, we'd receive two process calls - one from machinery type and one from our own type. -// Since we want this to be in-sync with machinery (as it's hidden type for machinery-based computers) we'll ignore -// non-relayed process calls. -/obj/item/modular_computer/processor/process(relayed = 0) - if(relayed) - ..() - else - return +/obj/item/device/modular_computer/processor/New(comp) + STOP_PROCESSING(SSobj, src) // Processed by its machine -/obj/item/modular_computer/processor/examine(var/mob/user) - if(damage > broken_damage) - user << "It is heavily damaged!" - else if(damage) - user << "It is damaged." - -// Power interaction is handled by our machinery part, due to machinery having APC connection. -/obj/item/modular_computer/processor/handle_power() - if(machinery_computer) - machinery_computer.handle_power() - -/obj/item/modular_computer/processor/New(comp) if(!comp || !istype(comp, /obj/machinery/modular_computer)) - CRASH("Inapropriate type passed to obj/item/modular_computer/processor/New()! Aborting.") + CRASH("Inapropriate type passed to obj/item/device/modular_computer/processor/New()! Aborting.") return // Obtain reference to machinery computer machinery_computer = comp @@ -48,72 +30,26 @@ steel_sheet_cost = machinery_computer.steel_sheet_cost max_damage = machinery_computer._max_damage broken_damage = machinery_computer._break_damage + base_active_power_usage = machinery_computer.base_active_power_usage + base_idle_power_usage = machinery_computer.base_idle_power_usage -/obj/item/modular_computer/processor/relay_qdel() +/obj/item/device/modular_computer/processor/relay_qdel() qdel(machinery_computer) -/obj/item/modular_computer/processor/find_hardware_by_name(N) - var/obj/item/weapon/computer_hardware/H = machinery_computer.find_hardware_by_name(N) - if(H) - return H - else - return ..() - -/obj/item/modular_computer/processor/update_icon() +/obj/item/device/modular_computer/processor/update_icon() if(machinery_computer) return machinery_computer.update_icon() -/obj/item/modular_computer/processor/get_header_data() - var/list/L = ..() - if(machinery_computer.tesla_link && machinery_computer.tesla_link.enabled && machinery_computer.powered()) - L["PC_apclinkicon"] = "charging.gif" - return L - -// Checks whether the machinery computer doesn't take power from APC network -/obj/item/modular_computer/processor/check_power_override() - if(!machinery_computer) - return 0 - if(!machinery_computer.tesla_link || !machinery_computer.tesla_link.enabled) - return 0 - return machinery_computer.powered() - // This thing is not meant to be used on it's own, get topic data from our machinery owner. -//obj/item/modular_computer/processor/canUseTopic(user, state) +//obj/item/device/modular_computer/processor/canUseTopic(user, state) // if(!machinery_computer) // return 0 // return machinery_computer.canUseTopic(user, state) -/obj/item/modular_computer/processor/shutdown_computer() +/obj/item/device/modular_computer/processor/shutdown_computer() if(!machinery_computer) return ..() machinery_computer.update_icon() - machinery_computer.use_power = 0 return - -/obj/item/modular_computer/processor/uninstall_component(mob/living/user, obj/item/weapon/computer_hardware/H, found = 0, critical = 0) - if(machinery_computer.tesla_link == H) - machinery_computer.tesla_link = null - var/obj/item/weapon/computer_hardware/tesla_link/L = H - L.holder = null - found = 1 - ..(user, H, found, critical) - -/obj/item/modular_computer/processor/get_all_components() - var/list/all_components = ..() - if(machinery_computer && 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(atom/neighbor) - if(!machinery_computer) - return 0 - return machinery_computer.Adjacent(neighbor) - -/obj/item/modular_computer/processor/turn_on(mob/user) - // If we have a tesla link on our machinery counterpart, enable it automatically. Lets computer without a battery work. - if(machinery_computer && machinery_computer.tesla_link) - machinery_computer.tesla_link.enabled = 1 - ..() \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index d03c25cca12..0c8cad838de 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -1,10 +1,11 @@ -/obj/item/modular_computer/tablet +/obj/item/device/modular_computer/tablet name = "tablet computer" icon = 'icons/obj/modular_tablet.dmi' icon_state = "tablet" icon_state_unpowered = "tablet" + icon_state_powered = "tablet" icon_state_menu = "menu" hardware_flag = PROGRAM_TABLET max_hardware_size = 1 w_class = 2 - var/use_power = 0 \ No newline at end of file + steel_sheet_cost = 1 diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index ffcb95ce57a..2cc4c700ab5 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -1,21 +1,21 @@ -// Available as custom loadout item, this is literally the worst possible cheap tablet -/obj/item/modular_computer/tablet/preset/cheap/New() - . = ..() +// This is literally the worst possible cheap tablet +/obj/item/device/modular_computer/tablet/preset/cheap desc = "A low-end tablet often seen among low ranked station personnel." - processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) - battery_module = new/obj/item/weapon/computer_hardware/battery_module/micro(src) - battery_module.charge_to_full() - hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/micro(src) - network_card = new/obj/item/weapon/computer_hardware/network_card(src) + +/obj/item/device/modular_computer/tablet/preset/cheap/New() + . = ..() + install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + install_component(new /obj/item/weapon/computer_hardware/battery(src, /obj/item/weapon/stock_parts/cell/computer/micro)) + install_component(new /obj/item/weapon/computer_hardware/hard_drive/small) + install_component(new /obj/item/weapon/computer_hardware/network_card) // Alternative version, an average one, for higher ranked positions mostly -/obj/item/modular_computer/tablet/preset/advanced/New() +/obj/item/device/modular_computer/tablet/preset/advanced/New() . = ..() - processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) - battery_module = new/obj/item/weapon/computer_hardware/battery_module(src) - battery_module.charge_to_full() - hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src) - network_card = new/obj/item/weapon/computer_hardware/network_card(src) - nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) - card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) \ No newline at end of file + install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + install_component(new /obj/item/weapon/computer_hardware/battery(src, /obj/item/weapon/stock_parts/cell/computer)) + install_component(new /obj/item/weapon/computer_hardware/hard_drive/small) + install_component(new /obj/item/weapon/computer_hardware/network_card) + install_component(new /obj/item/weapon/computer_hardware/card_slot) + install_component(new /obj/item/weapon/computer_hardware/printer/mini) diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index 0f6aec6558b..a4741b16745 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -1,4 +1,4 @@ -/obj/machinery/modular_computer/console/preset/ +/obj/machinery/modular_computer/console/preset // Can be changed to give devices specific hardware var/_has_id_slot = 0 var/_has_printer = 0 @@ -8,13 +8,14 @@ . = ..() if(!cpu) return - cpu.processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(cpu) + cpu.install_component(new /obj/item/weapon/computer_hardware/processor_unit) + if(_has_id_slot) - cpu.card_slot = new/obj/item/weapon/computer_hardware/card_slot(cpu) + cpu.install_component(new /obj/item/weapon/computer_hardware/card_slot) if(_has_printer) - cpu.nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(cpu) + cpu.install_component(new /obj/item/weapon/computer_hardware/printer) if(_has_battery) - cpu.battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(cpu) + cpu.install_component(new /obj/item/weapon/computer_hardware/battery(cpu, /obj/item/weapon/stock_parts/cell/computer/super)) install_programs() // Override in child types to install preset-specific programs. diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index 98b29951607..2aada0b5860 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -3,14 +3,14 @@ var/list/global_modular_computers = list() // Modular Computer - device that runs various programs and operates with hardware // DO NOT SPAWN THIS TYPE. Use /laptop/ or /console/ instead. -/obj/machinery/modular_computer/ +/obj/machinery/modular_computer name = "modular computer" desc = "An advanced computer." - 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 + use_power = 1 + idle_power_usage = 5 + 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 @@ -18,61 +18,20 @@ var/list/global_modular_computers = list() 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/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'. - 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/light_strength = 0 // Light luminosity when turned on - 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/icon_state_unpowered = null // Icon state when the computer is turned off. + var/icon_state_powered = null // Icon state when the computer is turned on. + 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/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'. + 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/light_strength = 0 // Light luminosity when turned on + 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/_max_damage = 100 var/_break_damage = 50 - 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. - -/obj/machinery/modular_computer/attack_ghost(mob/dead/observer/user) - if(cpu) - cpu.attack_ghost(user) - -/obj/machinery/modular_computer/emag_act(mob/user) - return cpu ? cpu.emag_act(user) : 1 - -/obj/machinery/modular_computer/update_icon() - icon_state = icon_state_unpowered - cut_overlays() - - if(!cpu || !cpu.enabled) - if (!(stat & NOPOWER) || battery_powered) - add_overlay(screen_icon_screensaver) - SetLuminosity(0) - return - SetLuminosity(light_strength) - if(cpu.active_program) - add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu) - else - overlays.Add(screen_icon_state_menu) - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/machinery/modular_computer/verb/eject_id() - set name = "Eject ID" - set category = "Object" - set src in view(1) - - if(cpu) - cpu.eject_id() - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/machinery/modular_computer/verb/eject_usb() - set name = "Eject Portable Device" - set category = "Object" - set src in view(1) - - if(cpu) - cpu.eject_usb() + var/obj/item/device/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things. /obj/machinery/modular_computer/New() ..() @@ -86,28 +45,67 @@ var/list/global_modular_computers = list() cpu = null return ..() +/obj/machinery/modular_computer/attack_ghost(mob/dead/observer/user) + if(cpu) + cpu.attack_ghost(user) + +/obj/machinery/modular_computer/emag_act(mob/user) + return cpu ? cpu.emag_act(user) : 1 + +/obj/machinery/modular_computer/update_icon() + cut_overlays() + icon_state = icon_state_powered + + if(!cpu || !cpu.enabled) + if (!(stat & NOPOWER) && (cpu && cpu.use_power())) + add_overlay(screen_icon_screensaver) + else + icon_state = icon_state_unpowered + SetLuminosity(0) + else + SetLuminosity(light_strength) + if(cpu.active_program) + add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu) + else + overlays.Add(screen_icon_state_menu) + + if(cpu && cpu.damage > cpu.broken_damage) + add_overlay("bsod") + add_overlay("broken") + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/machinery/modular_computer/verb/eject_id() + set name = "Eject ID" + set category = "Object" + set src in view(1) + + if(cpu) + cpu.eject_id() + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/machinery/modular_computer/verb/eject_disk() + set name = "Eject Data Disk" + set category = "Object" + set src in view(1) + + if(cpu) + cpu.eject_disk() + +/obj/machinery/modular_computer/AltClick(mob/user) + if(cpu) + cpu.AltClick(user) + // 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) cpu.attack_self(user) // CPU is an item, that's why we route attack_hand to attack_self -/obj/machinery/modular_computer/examine(mob/user) - . = ..() - if(cpu) - cpu.examine(user) - // Process currently calls handle_power(), may be expanded in future if more things are added. /obj/machinery/modular_computer/process() if(cpu) // Keep names in sync. cpu.name = src.name - cpu.process(1) - -// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null -/obj/machinery/modular_computer/proc/find_hardware_by_name(N) - if(tesla_link && (tesla_link.name == N)) - return tesla_link - return null + cpu.process() // Used in following function to reduce copypaste /obj/machinery/modular_computer/proc/power_failure(malfunction = 0) @@ -115,65 +113,18 @@ var/list/global_modular_computers = list() visible_message("\The [src]'s screen flickers [cpu.battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.") if(cpu) cpu.shutdown_computer(0) - battery_powered = 0 stat |= NOPOWER update_icon() -// Called by cpu item's process() automatically, handles our power interaction. -/obj/machinery/modular_computer/proc/handle_power() - if(!check_external_power()) - if(cpu.battery_module && cpu.battery_module.battery.charge <= 0)// Battery-run but battery is depleted. - power_failure() - return 0 - else if(!cpu.battery_module) // Not battery run, but lacking APC connection. - power_failure() - return 0 - else if(stat & NOPOWER) - stat &= ~NOPOWER - - if(cpu.battery_module && cpu.battery_module.battery.charge) - battery_powered = 1 - else - battery_powered = 0 - - var/power_usage = cpu.screen_on ? base_active_power_usage : base_idle_power_usage - for(var/C in src.cpu.get_all_components()) - var/obj/item/weapon/computer_hardware/CH = C - if(CH.enabled) - power_usage += CH.power_usage - - // Wireless APC connection exists. - if(tesla_link && tesla_link.enabled && tesla_link.check_functionality()) - idle_power_usage = power_usage - 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_module && (cpu.battery_module.battery.charge < cpu.battery_module.battery.maxcharge)) - use_power = 2 - - 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. - if(!cpu.battery_module.battery.use(idle_power_usage * CELLRATE)) - cpu.battery_module.battery.charge = 0 - power_failure() - - else // No wireless connection run only on battery. - use_power = 0 - if (cpu.battery_module) - if(!cpu.battery_module.check_functionality()) - power_failure(1) - return - if(!cpu.battery_module.battery.use(power_usage * CELLRATE)) - cpu.battery_module.battery.charge = 0 - power_failure() - 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. /obj/machinery/modular_computer/power_change() - if(battery_powered) + if(cpu && cpu.use_power()) // If "CPU" still has a power source, PC wouldn't go offline. + stat &= ~NOPOWER + update_icon() return ..() + update_icon() /obj/machinery/modular_computer/attackby(var/obj/item/weapon/W as obj, mob/user) if(cpu) @@ -197,11 +148,4 @@ var/list/global_modular_computers = list() // "Brute" damage mostly damages the casing. /obj/machinery/modular_computer/bullet_act(obj/item/projectile/Proj) if(cpu) - cpu.bullet_act(Proj) - - -/obj/machinery/modular_computer/proc/check_external_power() - if((!powered() || !tesla_link || !tesla_link.enabled || !tesla_link.check_functionality())) - return 0 - else - return 1 \ No newline at end of file + cpu.bullet_act(Proj) \ No newline at end of file diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm index 5742faa9311..b5b6fe7b143 100644 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -1,10 +1,11 @@ -/obj/machinery/modular_computer/console/ +/obj/machinery/modular_computer/console name = "console" desc = "A stationary computer." icon = 'icons/obj/modular_console.dmi' icon_state = "console" - icon_state_unpowered = "console" + icon_state_powered = "console" + icon_state_unpowered = "console-off" screen_icon_state_menu = "menu" hardware_flag = PROGRAM_CONSOLE var/console_department = "" // Used in New() to set network tag according to our area. @@ -12,7 +13,7 @@ density = 1 base_idle_power_usage = 100 base_active_power_usage = 500 - max_hardware_size = 3 + max_hardware_size = 4 steel_sheet_cost = 10 light_strength = 2 _max_damage = 300 @@ -21,16 +22,19 @@ /obj/machinery/modular_computer/console/buildable/New() ..() // User-built consoles start as empty frames. - qdel(tesla_link) + qdel(cpu.recharger) qdel(cpu.network_card) qdel(cpu.hard_drive) /obj/machinery/modular_computer/console/New() ..() - 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) - cpu.hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(src) // Consoles generally have better HDDs due to lower space limitations + if(cpu.battery_module) + qdel(cpu.battery_module) + + cpu.install_component(new /obj/item/weapon/computer_hardware/network_card/wired) + cpu.install_component(new /obj/item/weapon/computer_hardware/recharger/APC) + cpu.install_component(new /obj/item/weapon/computer_hardware/hard_drive/super) // 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) diff --git a/code/modules/modular_computers/computers/machinery/modular_laptop.dm b/code/modules/modular_computers/computers/machinery/modular_laptop.dm deleted file mode 100644 index 55cb385a4e1..00000000000 --- a/code/modules/modular_computers/computers/machinery/modular_laptop.dm +++ /dev/null @@ -1,110 +0,0 @@ -// Laptop in it's item state, can be carried around. - -/obj/item/laptop - name = "laptop computer" - desc = "A portable computer. It is closed." - icon = 'icons/obj/modular_laptop.dmi' - icon_state = "laptop-closed" - item_state = "laptop-inhand" - w_class = 3 - var/obj/machinery/modular_computer/laptop/stored_computer = null - -/obj/item/laptop/verb/open_computer() - set name = "Open Laptop" - set category = "Object" - set src in view(1) - - if(usr.stat || usr.restrained() || usr.lying || !istype(usr, /mob/living)) - usr << "You can't do that." - return - - if(!Adjacent(usr)) - usr << "You can't reach it." - return - - if(!istype(loc,/turf)) - usr << "[src] is too bulky! You'll have to set it down." - return - - if(!stored_computer) - if(contents.len) - for(var/obj/O in contents) - O.forceMove(src.loc) - usr << "\The [src] crumbles to pieces." - spawn(5) - qdel(src) - return - - stored_computer.forceMove(src.loc) - stored_computer.stat &= ~MAINT - stored_computer.update_icon() - if(stored_computer.cpu) - stored_computer.cpu.screen_on = 1 - loc = stored_computer - usr << "You open \the [src]." - - -/obj/item/laptop/AltClick() - if(Adjacent(usr)) - open_computer() - -// The actual laptop -/obj/machinery/modular_computer/laptop - name = "laptop computer" - desc = "A portable computer." - var/obj/item/laptop/portable = null // Portable version of this computer, dropped on alt-click to allow transport. Used by laptops. - hardware_flag = PROGRAM_LAPTOP - icon_state_unpowered = "laptop-open" // Icon state when the computer is turned off - icon = 'icons/obj/modular_laptop.dmi' - icon_state = "laptop-open" - base_idle_power_usage = 25 - base_active_power_usage = 200 - max_hardware_size = 2 - light_strength = 3 - _max_damage = 200 - _break_damage = 100 - -/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() - set name = "Close Laptop" - set category = "Object" - set src in view(1) - - if(usr.stat || usr.restrained() || usr.lying || !istype(usr, /mob/living)) - usr << "You can't do that." - return - - if(!Adjacent(usr)) - usr << "You can't reach it." - return - - close_laptop(usr) - -/obj/machinery/modular_computer/laptop/proc/close_laptop(mob/user = null) - if(istype(loc,/obj/item/laptop)) - return - if(!istype(loc,/turf)) - return - - if(!portable) - portable=new - portable.stored_computer = src - - portable.forceMove(src.loc) - src.forceMove(portable) - stat |= MAINT - if(user) - user << "You close \the [src]." - if(cpu) - cpu.screen_on = 0 - -/obj/machinery/modular_computer/laptop/AltClick() - if(Adjacent(usr)) - close_laptop() \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index c853e7ce0fd..c2b809ad46a 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -20,8 +20,8 @@ var/global/file_uid = 0 holder.remove_file(src) // holder.holder is the computer that has drive installed. If we are Destroy()ing program that's currently running kill it. - if(holder.holder2 && holder.holder2.active_program == src) - holder.holder2.kill_program(1) + if(holder.holder && holder.holder.active_program == src) + holder.holder.kill_program(forced = TRUE) holder = null ..() diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 87aa3dab1ab..df2967bbd56 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -5,7 +5,7 @@ var/required_access = null // List of required accesses to *run* the program. var/transfer_access = null // List of required access to download or file host the program var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. - var/obj/item/modular_computer/computer // Device that runs this program. + var/obj/item/device/modular_computer/computer // Device that runs this program. var/filedesc = "Unknown Program" // User-friendly name of this program. var/extended_desc = "N/A" // Short description of this program's function. var/program_icon_state = null // Program-specific screen icon state @@ -16,10 +16,9 @@ var/network_destination = null // Optional string that describes what NTNet server/system this program connects to. Used in default logging. var/available_on_ntnet = 1 // Whether the program can be downloaded from NTNet. Set to 0 to disable. var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. - var/computer_emagged = 0 // Set to 1 if computer that's running us was emagged. Computer updates this every Process() tick var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images! -/datum/computer_file/program/New(obj/item/modular_computer/comp = null) +/datum/computer_file/program/New(obj/item/device/modular_computer/comp = null) ..() if(comp && istype(comp)) computer = comp @@ -78,7 +77,7 @@ if(!access_to_check) // No required_access, allow it. return 1 - if(computer_emagged && !transfer) //emags can bypass the execution locks but not the download ones. + if(computer.emagged && !transfer) //emags can bypass the execution locks but not the download ones. return 1 if(IsAdminGhost(user)) @@ -91,9 +90,8 @@ var/mob/living/carbon/human/h = user var/obj/item/weapon/card/id/I = h.get_idcard() var/obj/item/weapon/card/id/C = h.get_active_hand() - if (istype(C, /obj/item/device/pda)) - var/obj/item/device/pda/pda = C - C = pda.id + if(C) + C = C.GetID() if(!(C && istype(C))) C = null @@ -130,7 +128,7 @@ return 0 // Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. -/datum/computer_file/program/proc/kill_program(forced = 0) +/datum/computer_file/program/proc/kill_program(forced = FALSE) program_state = PROGRAM_STATE_KILLED if(network_destination) generate_network_log("Connection to [network_destination] closed.") diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm index 996d6393722..279d646cfd7 100644 --- a/code/modules/modular_computers/file_system/program_events.dm +++ b/code/modules/modular_computers/file_system/program_events.dm @@ -7,11 +7,11 @@ // Called when the computer fails due to power loss. Override when program wants to specifically react to power loss. /datum/computer_file/program/proc/event_powerfailure(background) - kill_program(1) + kill_program(forced = TRUE) // Called when the network connectivity fails. Computer does necessary checks and only calls this when requires_ntnet_feature and similar variables are not met. /datum/computer_file/program/proc/event_networkfailure(background) - kill_program(1) + kill_program(forced = TRUE) if(background) computer.visible_message("\The [computer]'s screen displays an \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error") else diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index 3a3c9d72e78..57aef75feb4 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -101,6 +101,6 @@ . = ..(user) alarmdisplay += src -/datum/computer_file/program/alarm_monitor/kill_program(forced = 0) +/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE) alarmdisplay -= src - ..(forced) \ No newline at end of file + ..() \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index a3eb9a27354..d14bb885bfe 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -28,13 +28,13 @@ target = null error = "Connection to destination relay lost." -/datum/computer_file/program/ntnet_dos/kill_program(var/forced) +/datum/computer_file/program/ntnet_dos/kill_program(forced = FALSE) if(target) target.dos_sources.Remove(src) target = null executed = 0 - ..(forced) + ..() /datum/computer_file/program/ntnet_dos/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 91b61d37836..f5ae0766f74 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -26,13 +26,13 @@ computer.visible_message("\The [computer]'s battery explodes in rain of sparks.") var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread spark_system.start() - if(istype(computer, /obj/item/modular_computer/processor)) - var/obj/item/modular_computer/processor/P = computer - if(P.machinery_computer.tesla_link && prob(50)) - qdel(P.machinery_computer.tesla_link) - computer.visible_message("\The [computer]'s tesla link explodes in rain of sparks.") - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread - spark_system.start() + + if(computer.recharger && prob(50)) + qdel(computer.recharger) + computer.visible_message("\The [computer]'s recharger explodes in rain of sparks.") + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread + spark_system.start() + /datum/computer_file/program/revelation/ui_act(action, params) if(..()) diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index feb688b1d2a..1e201da470b 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -129,7 +129,7 @@ else show_assignments = 1 if("PRG_print") - if(computer && computer.nano_printer) //This option should never be called if there is no printer + if(computer && computer.printer) //This option should never be called if there is no printer if(mod_mode) if(authorized()) var/contents = {"