Files
Bubberstation/code/modules/modular_computers/computers/item/computer.dm
John Willard 91f02f2a6b canUseTopic now uses TRUE/FALSE instead of defines that just say TRUE (#69790)
* canUseTopic now uses TRUE/FALSE instead of defines that just say TRUE

The most idiotic thing I've seen is canUseTopic's defines, they literally just define TRUE, you can use it however you want, it doesn't matter, it just means TRUE. You can mix and match the args and it will set that arg to true, despite the name.

It's so idiotic I decided to remove it, so now I can reclaim a little bit of my sanity.
2022-10-01 09:47:52 -07:00

814 lines
29 KiB
Plaintext

GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar to GLOB.PDAs (used primarily with ntmessenger.dm)
// 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."
icon = 'icons/obj/computer.dmi'
icon_state = "laptop-open"
light_on = FALSE
integrity_failure = 0.5
max_integrity = 100
armor = list(MELEE = 0, BULLET = 20, LASER = 20, ENERGY = 100, BOMB = 0, BIO = 0, FIRE = 0, ACID = 0)
light_system = MOVABLE_LIGHT_DIRECTIONAL
var/bypass_state = FALSE // bypassing the set icon state
var/enabled = 0 // Whether the computer is turned on.
var/upgradable = TRUE // whether or not the computer can be upgraded
var/deconstructable = TRUE // whether or not the computer can be deconstructed
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
///The type of device this computer is, as a flag
var/hardware_flag = NONE
// Options: PROGRAM_ALL PROGRAM_CONSOLE | | PROGRAM_LAPTOP | PROGRAM_TABLET
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
///Looping sound for when the computer is on
var/datum/looping_sound/computer/soundloop
///Whether or not this modular computer uses the looping sound
var/looping_sound = TRUE
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.
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/display_overlays = TRUE // If FALSE, don't draw overlays on this device at all
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.
/// Amount of programs that can be ran at once
var/max_idle_programs = 2
/// List of "connection ports" in this computer and the components with which they are plugged
var/list/all_components = list()
/// Lazy List of extra hardware slots that can be used modularly.
var/list/expansion_bays
/// Number of total expansion bays this computer has available.
var/max_bays = 0
var/saved_identification = null // next two values are the currently imprinted id and job values
var/saved_job = null
/// Allow people with chunky fingers to use?
var/allow_chunky = FALSE
var/honkamnt = 0 /// honk honk honk honk honk honkh onk honkhnoohnk
var/list/idle_threads // 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.
var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed
/// How far the computer's light can reach, is not editable by players.
var/comp_light_luminosity = 3
/// The built-in light's color, editable by players.
var/comp_light_color = "#FFFFFF"
var/invisible = FALSE // whether or not the tablet is invisible in messenger and other apps
var/datum/picture/saved_image // the saved image used for messaging purpose like come on dude
/// Stored pAI in the computer
var/obj/item/pai_card/inserted_pai = null
///The amount of paper currently stored in the PDA
var/stored_paper = 10
///The max amount of paper that can be held at once.
var/max_paper = 30
/obj/item/modular_computer/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
if(!physical)
physical = src
set_light_color(comp_light_color)
set_light_range(comp_light_luminosity)
idle_threads = list()
if(looping_sound)
soundloop = new(src, enabled)
UpdateDisplay()
if(has_light)
add_item_action(/datum/action/item_action/toggle_computer_light)
update_appearance()
register_context()
init_network_id(NETWORK_TABLETS)
Add_Messenger()
/obj/item/modular_computer/Destroy()
wipe_program(forced = TRUE)
for(var/datum/computer_file/program/idle as anything in idle_threads)
idle.kill_program(TRUE)
idle_threads?.Cut()
STOP_PROCESSING(SSobj, src)
for(var/port in all_components)
var/obj/item/computer_hardware/component = all_components[port]
qdel(component)
all_components?.Cut()
//Some components will actually try and interact with this, so let's do it later
QDEL_NULL(soundloop)
Remove_Messenger()
if(istype(inserted_pai))
QDEL_NULL(inserted_pai)
physical = null
return ..()
/obj/item/modular_computer/pre_attack_secondary(atom/A, mob/living/user, params)
if(active_program?.tap(A, user, params))
user.do_attack_animation(A) //Emulate this animation since we kill the attack in three lines
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1) //Likewise for the tap sound
addtimer(CALLBACK(src, .proc/play_ping), 0.5 SECONDS, TIMER_UNIQUE) //Slightly delayed ping to indicate success
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return ..()
// shameless copy of newscaster photo saving
/obj/item/modular_computer/proc/save_photo(icon/photo)
var/photo_file = copytext_char(md5("\icon[photo]"), 1, 6)
if(!fexists("[GLOB.log_directory]/photos/[photo_file].png"))
//Clean up repeated frames
var/icon/clean = new /icon()
clean.Insert(photo, "", SOUTH, 1, 0)
fcopy(clean, "[GLOB.log_directory]/photos/[photo_file].png")
return photo_file
/**
* Plays a ping sound.
*
* Timers runtime if you try to make them call playsound. Yep.
*/
/obj/item/modular_computer/proc/play_ping()
playsound(loc, 'sound/machines/ping.ogg', get_clamped_volume(), FALSE, -1)
/obj/item/modular_computer/AltClick(mob/user)
..()
if(issilicon(user))
return
if(user.canUseTopic(src, be_close = TRUE))
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
if(card_slot2?.try_eject(user) || card_slot?.try_eject(user))
return TRUE
if(!istype(src, /obj/item/modular_computer/tablet))
return FALSE
// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs.
/obj/item/modular_computer/GetAccess()
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
if(card_slot)
return card_slot.GetAccess()
return ..()
/obj/item/modular_computer/GetID()
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/card/id/first_id = card_slot?.GetID()
var/obj/item/card/id/second_id = card_slot2?.GetID()
// We have two IDs, pick the one with the most command accesses, preferring the primary slot.
if(first_id && second_id)
var/first_id_tally = SSid_access.tally_access(first_id, ACCESS_FLAG_COMMAND)
var/second_id_tally = SSid_access.tally_access(second_id, ACCESS_FLAG_COMMAND)
return (first_id_tally >= second_id_tally) ? first_id : second_id
// If we don't have both ID slots filled, pick the one that is filled.
if(first_id)
return first_id
if(second_id)
return second_id
// Otherwise, we have no ID at all.
return ..()
/obj/item/modular_computer/get_id_examine_strings(mob/user)
. = ..()
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
var/obj/item/card/id/id_card1 = card_slot?.GetID()
var/obj/item/card/id/id_card2 = card_slot2?.GetID()
if(id_card1 || id_card2)
if(id_card1 && id_card2)
. += "\The [src] is displaying [id_card1] and [id_card2]."
var/list/id_icons = list()
id_icons += id_card1.get_id_examine_strings(user)
id_icons += id_card2.get_id_examine_strings(user)
. += id_icons.Join(" ")
else if(id_card1)
. += "\The [src] is displaying [id_card1]."
. += id_card1.get_id_examine_strings(user)
else
. += "\The [src] is displaying [id_card2]."
. += id_card2.get_id_examine_strings(user)
/obj/item/modular_computer/RemoveID()
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
var/removed_id = (card_slot2?.try_eject() || card_slot?.try_eject())
if(removed_id)
if(ishuman(loc))
var/mob/living/carbon/human/human_wearer = loc
if(human_wearer.wear_id == src)
human_wearer.sec_hud_set_ID()
update_slot_icon()
update_appearance()
return removed_id
return ..()
/obj/item/modular_computer/proc/print_text(text_to_print, paper_title = "")
if(!stored_paper)
return FALSE
var/obj/item/paper/printed_paper = new /obj/item/paper(drop_location())
printed_paper.add_raw_text(text_to_print)
if(paper_title)
printed_paper.name = paper_title
printed_paper.update_appearance()
stored_paper--
return TRUE
/obj/item/modular_computer/InsertID(obj/item/inserting_item)
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
if(!(card_slot || card_slot2))
return FALSE
var/obj/item/card/inserting_id = inserting_item.GetID()
if(!inserting_id)
return FALSE
if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id)))
if(ishuman(loc))
var/mob/living/carbon/human/human_wearer = loc
if(human_wearer.wear_id == src)
human_wearer.sec_hud_set_ID()
update_appearance()
update_slot_icon()
return TRUE
/obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location)
var/mob/M = usr
if((!istype(over_object, /atom/movable/screen)) && usr.canUseTopic(src, be_close = TRUE))
return attack_self(M)
return ..()
/obj/item/modular_computer/attack_ai(mob/user)
return attack_self(user)
/obj/item/modular_computer/attack_ghost(mob/dead/observer/user)
. = ..()
if(.)
return
if(enabled)
ui_interact(user)
else if(isAdminGhostAI(user))
var/response = tgui_alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", list("Yes", "No"))
if(response == "Yes")
turn_on(user)
/obj/item/modular_computer/emag_act(mob/user)
if(!enabled)
to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
obj_flags |= EMAGGED //Mostly for consistancy purposes; the programs will do their own emag handling
var/newemag = FALSE
var/obj/item/computer_hardware/hard_drive/drive = all_components[MC_HDD]
for(var/datum/computer_file/program/app in drive.stored_files)
if(!istype(app))
continue
if(app.run_emag())
newemag = TRUE
if(newemag)
to_chat(user, span_notice("You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past."))
return TRUE
to_chat(user, span_notice("You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it."))
return FALSE
/obj/item/modular_computer/examine(mob/user)
. = ..()
if(atom_integrity <= integrity_failure * max_integrity)
. += span_danger("It is heavily damaged!")
else if(atom_integrity < max_integrity)
. += span_warning("It is damaged.")
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/multiple_slots = istype(card_slot) && istype(card_slot2)
if(card_slot)
if(card_slot.stored_card || card_slot2?.stored_card)
var/obj/item/card/id/first_ID = card_slot?.stored_card
var/obj/item/card/id/second_ID = card_slot2?.stored_card
var/multiple_cards = (first_ID && second_ID)
if(Adjacent(user))
. += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID || second_ID]"]."
else
. += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
. += span_info("Alt-click [src] to eject the identification card[multiple_cards ? "s":""].")
else
. += "It has [multiple_slots ? "two slots" : "a slot"] installed for identification cards."
/obj/item/modular_computer/examine_more(mob/user)
. = ..()
var/obj/item/computer_hardware/hard_drive/hdd = all_components[MC_HDD]
if(hdd)
for(var/datum/computer_file/app_examine as anything in hdd.stored_files)
if(app_examine.on_examine(src, user))
. += app_examine.on_examine(src, user)
if(Adjacent(user))
. += span_notice("Paper level: [stored_paper] / [max_paper].")
/obj/item/modular_computer/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
context[SCREENTIP_CONTEXT_ALT_LMB] = "Remove ID"
context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB] = "Remove Disk"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/modular_computer/update_icon_state()
if(!bypass_state)
icon_state = enabled ? icon_state_powered : icon_state_unpowered
return ..()
/obj/item/modular_computer/update_overlays()
. = ..()
var/init_icon = initial(icon)
if(!init_icon)
return
if(!display_overlays)
return
if(enabled)
. += active_program ? mutable_appearance(init_icon, active_program.program_icon_state) : mutable_appearance(init_icon, icon_state_menu)
if(atom_integrity <= integrity_failure * max_integrity)
. += mutable_appearance(init_icon, "bsod")
. += mutable_appearance(init_icon, "broken")
// On-click handling. Turns on the computer if it's off and opens the GUI.
/obj/item/modular_computer/interact(mob/user)
if(enabled)
ui_interact(user)
else
turn_on(user)
/obj/item/modular_computer/CtrlShiftClick(mob/user)
. = ..()
if(.)
return
var/obj/item/computer_hardware/hard_drive/ssd = all_components[MC_SDD]
if(!ssd)
return
if(uninstall_component(ssd, usr))
user.put_in_hands(ssd)
playsound(src, 'sound/machines/card_slide.ogg', 50)
/obj/item/modular_computer/proc/turn_on(mob/user, open_ui = TRUE)
var/issynth = issilicon(user) // Robots and AIs get different activation messages.
if(atom_integrity <= integrity_failure * max_integrity)
if(issynth)
to_chat(user, span_warning("You send an activation signal to \the [src], but it responds with an error code. It must be damaged."))
else
to_chat(user, span_warning("You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again."))
return FALSE
if(use_power()) // use_power() checks if the PC is powered
if(issynth)
to_chat(user, span_notice("You send an activation signal to \the [src], turning it on."))
else
to_chat(user, span_notice("You press the power button and start up \the [src]."))
if(looping_sound)
soundloop.start()
enabled = 1
update_appearance()
if(open_ui)
ui_interact(user)
return TRUE
else // Unpowered
if(issynth)
to_chat(user, span_warning("You send an activation signal to \the [src] but it does not respond."))
else
to_chat(user, span_warning("You press the power button but \the [src] does not respond."))
return FALSE
// Process currently calls handle_power(), may be expanded in future if more things are added.
/obj/item/modular_computer/process(delta_time)
if(!enabled) // The computer is turned off
last_power_usage = 0
return
if(atom_integrity <= integrity_failure * max_integrity)
shutdown_computer()
return
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(delta_time)
active_program.ntnet_status = get_ntnet_status()
else
active_program = null
for(var/datum/computer_file/program/P as anything in idle_threads)
if(P.program_state != PROGRAM_STATE_KILLED)
P.process_tick(delta_time)
P.ntnet_status = get_ntnet_status()
else
idle_threads.Remove(P)
handle_power(delta_time) // Handles all computer power interaction
//check_update_ui_need()
/**
* Displays notification text alongside a soundbeep when requested to by a program.
*
* After checking that the requesting program is allowed to send an alert, creates
* a visible message of the requested text alongside a soundbeep. This proc adds
* text to indicate that the message is coming from this device and the program
* on it, so the supplied text should be the exact message and ending punctuation.
*
* Arguments:
* The program calling this proc.
* The message that the program wishes to display.
*/
/obj/item/modular_computer/proc/alert_call(datum/computer_file/program/caller, alerttext, sound = 'sound/machines/twobeep_high.ogg')
if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
return FALSE
playsound(src, sound, 50, TRUE)
visible_message(span_notice("[icon2html(src)] [span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]")]"))
/obj/item/modular_computer/proc/ring(ringtone) // bring bring
if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED))
playsound(src, pick('sound/machines/twobeep_voice1.ogg', 'sound/machines/twobeep_voice2.ogg'), 50, TRUE)
else
playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE)
visible_message("*[ringtone]*")
/obj/item/modular_computer/proc/send_sound()
playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE)
// 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()
data["PC_device_theme"] = device_theme
var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL]
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
switch(get_ntnet_status())
if(NTNET_NO_SIGNAL)
data["PC_ntneticon"] = "sig_none.gif"
if(NTNET_LOW_SIGNAL)
data["PC_ntneticon"] = "sig_low.gif"
if(NTNET_GOOD_SIGNAL)
data["PC_ntneticon"] = "sig_high.gif"
if(NTNET_ETHERNET_SIGNAL)
data["PC_ntneticon"] = "sig_lan.gif"
if(length(idle_threads))
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"] = station_time_timestamp()
data["PC_hasheader"] = 1
data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen
return data
///Wipes the computer's current program. Doesn't handle any of the niceties around doing this
/obj/item/modular_computer/proc/wipe_program(forced)
if(!active_program)
return
active_program.kill_program(forced)
active_program = null
// Relays kill program request to currently active program. Use this to quit current program.
/obj/item/modular_computer/proc/kill_program(forced = FALSE)
wipe_program(forced)
var/mob/user = usr
if(user && istype(user))
//Here to prevent programs sleeping in destroy
INVOKE_ASYNC(src, /datum/proc/ui_interact, user) // Re-open the UI on this computer. It should show the main screen now.
update_appearance()
/obj/item/modular_computer/proc/open_program(mob/user, datum/computer_file/program/program)
if(program.computer != src)
CRASH("tried to open program that does not belong to this computer")
if(!program || !istype(program)) // Program not found or it's not executable program.
to_chat(user, span_danger("\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning."))
return FALSE
if(!program.is_supported_by_hardware(hardware_flag, 1, user))
return FALSE
// The program is already running. Resume it.
if(program in idle_threads)
program.program_state = PROGRAM_STATE_ACTIVE
active_program = program
program.alert_pending = FALSE
idle_threads.Remove(program)
update_appearance()
updateUsrDialog()
return TRUE
if(idle_threads.len > max_idle_programs)
to_chat(user, span_danger("\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error."))
return FALSE
if(program.requires_ntnet && !get_ntnet_status(program.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet.
to_chat(user, span_danger("\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning."))
return FALSE
if(!program.on_start(user))
return FALSE
active_program = program
program.alert_pending = FALSE
update_appearance()
updateUsrDialog()
return TRUE
// 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(!SSnetworks.station_network || !SSnetworks.station_network.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal.
return NTNET_NO_SIGNAL
// computers are connected through ethernet
if(hardware_flag & PROGRAM_CONSOLE)
return NTNET_ETHERNET_SIGNAL
var/turf/current_turf = get_turf(src)
if(!current_turf || !istype(current_turf))
return NTNET_NO_SIGNAL
if(is_station_level(current_turf.z))
if(hardware_flag & PROGRAM_LAPTOP) //laptops can connect to ethernet but they have to be on station for that
return NTNET_ETHERNET_SIGNAL
return NTNET_GOOD_SIGNAL
else if(is_mining_level(current_turf.z))
return NTNET_LOW_SIGNAL
return NTNET_NO_SIGNAL
/obj/item/modular_computer/proc/add_log(text)
if(!get_ntnet_status())
return FALSE
return SSnetworks.add_log(text, network_id)
/obj/item/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(looping_sound)
soundloop.stop()
if(physical && loud)
physical.visible_message(span_notice("\The [src] shuts down."))
enabled = FALSE
update_appearance()
/obj/item/modular_computer/ui_action_click(mob/user, actiontype)
if(istype(actiontype, /datum/action/item_action/toggle_computer_light))
toggle_flashlight()
return
return ..()
/**
* Toggles the computer's flashlight, if it has one.
*
* Called from ui_act(), does as the name implies.
* It is separated from ui_act() to be overwritten as needed.
*/
/obj/item/modular_computer/proc/toggle_flashlight()
if(!has_light)
return FALSE
set_light_on(!light_on)
update_appearance()
update_action_buttons(force = TRUE) //force it because we added an overlay, not changed its icon
return TRUE
/**
* Sets the computer's light color, if it has a light.
*
* Called from ui_act(), this proc takes a color string and applies it.
* It is separated from ui_act() to be overwritten as needed.
* Arguments:
** color is the string that holds the color value that we should use. Proc auto-fails if this is null.
*/
/obj/item/modular_computer/proc/set_flashlight_color(color)
if(!has_light || !color)
return FALSE
comp_light_color = color
set_light_color(color)
return TRUE
/obj/item/modular_computer/proc/UpdateDisplay()
name = "[saved_identification] ([saved_job])"
/obj/item/modular_computer/attackby(obj/item/attacking_item, mob/user, params)
// Check for ID first
if(isidcard(attacking_item) && InsertID(attacking_item))
return
// Check for cash next
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
if(card_slot && iscash(attacking_item))
var/obj/item/card/id/inserted_id = card_slot.GetID()
if(inserted_id)
inserted_id.attackby(attacking_item, user) // If we do, try and put that attacking object in
return
// Inserting a pAI
if(istype(attacking_item, /obj/item/pai_card) && !inserted_pai)
if(!user.transferItemToLoc(attacking_item, src))
return
inserted_pai = attacking_item
to_chat(user, span_notice("You slot \the [attacking_item] into [src]."))
return
// Check if any Applications need it
var/obj/item/computer_hardware/hard_drive/hdd = all_components[MC_HDD]
if(hdd)
for(var/datum/computer_file/item_holding_app as anything in hdd.stored_files)
if(item_holding_app.try_insert(attacking_item, user))
return
if(istype(attacking_item, /obj/item/paper))
if(stored_paper >= max_paper)
to_chat(user, span_warning("You try to add \the [attacking_item] into [src], but it can't hold any more!"))
return
if(!user.temporarilyRemoveItemFromInventory(attacking_item))
return FALSE
to_chat(user, span_notice("You insert \the [attacking_item] into [src]'s paper recycler."))
qdel(attacking_item)
stored_paper++
return
if(istype(attacking_item, /obj/item/paper_bin))
var/obj/item/paper_bin/bin = attacking_item
if(bin.total_paper <= 0)
to_chat(user, span_notice("\The [bin] is empty!"))
return
var/papers_added //just to keep track
while((bin.total_paper > 0) && (stored_paper < max_paper))
papers_added++
stored_paper++
bin.remove_paper()
if(!papers_added)
return
to_chat(user, span_notice("Added in [papers_added] new sheets. You now have [stored_paper] / [max_paper] printing paper stored."))
bin.update_appearance()
return
// Insert items into the components
for(var/h in all_components)
var/obj/item/computer_hardware/H = all_components[h]
if(H.try_insert(attacking_item, user))
return
// Insert new hardware
if(istype(attacking_item, /obj/item/computer_hardware) && upgradable)
if(install_component(attacking_item, user))
playsound(src, 'sound/machines/card_slide.ogg', 50)
return
return ..()
/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool)
. = ..()
if(!deconstructable)
return
if(!length(all_components))
balloon_alert(user, "no components installed!")
return
var/list/component_names = list()
for(var/h in all_components)
var/obj/item/computer_hardware/H = all_components[h]
component_names.Add(H.name)
var/choice = tgui_input_list(user, "Component to uninstall", "Computer maintenance", sort_list(component_names))
if(isnull(choice))
return
if(!Adjacent(user))
return
var/obj/item/computer_hardware/H = find_hardware_by_name(choice)
if(!H)
return TOOL_ACT_TOOLTYPE_SUCCESS
tool.play_tool_sound(src, user, 20, volume=20)
uninstall_component(H, user)
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/item/modular_computer/wrench_act(mob/living/user, obj/item/tool)
. = ..()
if(length(all_components))
balloon_alert(user, "remove the other components!")
return TOOL_ACT_TOOLTYPE_SUCCESS
tool.play_tool_sound(src, user, 20, volume=20)
new /obj/item/stack/sheet/iron(get_turf(loc), steel_sheet_cost)
user.balloon_alert(user, "disassembled")
relay_qdel()
qdel(src)
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/item/modular_computer/welder_act(mob/living/user, obj/item/tool)
. = ..()
if(atom_integrity == max_integrity)
to_chat(user, span_warning("\The [src] does not require repairs."))
return TOOL_ACT_TOOLTYPE_SUCCESS
if(!tool.tool_start_check(user, amount=1))
return TOOL_ACT_TOOLTYPE_SUCCESS
to_chat(user, span_notice("You begin repairing damage to \the [src]..."))
if(!tool.use_tool(src, user, 20, volume=50, amount=1))
return TOOL_ACT_TOOLTYPE_SUCCESS
atom_integrity = max_integrity
to_chat(user, span_notice("You repair \the [src]."))
update_appearance()
return TOOL_ACT_TOOLTYPE_SUCCESS
// Used by processor to relay qdel() to machinery type.
/obj/item/modular_computer/proc/relay_qdel()
return
// Perform adjacency checks on our physical counterpart, if any.
/obj/item/modular_computer/Adjacent(atom/neighbor)
if(physical && physical != src)
return physical.Adjacent(neighbor)
return ..()
/obj/item/modular_computer/proc/Add_Messenger()
GLOB.TabletMessengers += src
/obj/item/modular_computer/proc/Remove_Messenger()
GLOB.TabletMessengers -= src