From 5b0a1077f42730d9f074a9da00d84958dc6f53cc Mon Sep 17 00:00:00 2001 From: Letter N <24603524+LetterN@users.noreply.github.com> Date: Fri, 12 Feb 2021 18:29:00 +0800 Subject: [PATCH] Modular Cmp Updates --- .../datums/looping_sounds/machinery_sounds.dm | 24 +- .../computers/_modular_computer_shared.dm | 4 +- .../computers/item/computer.dm | 137 ++++++--- .../computers/item/computer_power.dm | 8 +- .../computers/item/computer_ui.dm | 36 ++- .../computers/item/laptop.dm | 17 +- .../computers/item/processor.dm | 13 +- .../computers/item/tablet.dm | 97 ++++++ .../computers/item/tablet_presets.dm | 13 +- .../computers/machinery/modular_computer.dm | 24 +- .../computers/machinery/modular_console.dm | 2 +- .../modular_computers/file_system/program.dm | 72 +++-- .../file_system/programs/airestorer.dm | 16 +- .../file_system/programs/alarm.dm | 1 + .../programs/antagonist/contract_uplink.dm | 6 +- .../file_system/programs/antagonist/dos.dm | 4 +- .../programs/antagonist/revelation.dm | 10 +- .../file_system/programs/arcade.dm | 38 ++- .../file_system/programs/atmosscan.dm | 6 +- .../file_system/programs/borg_monitor.dm | 8 +- .../file_system/programs/bounty_board.dm | 3 +- .../file_system/programs/budgetordering.dm | 280 ++++++++++++++++++ .../file_system/programs/card.dm | 37 +-- .../file_system/programs/cargoship.dm | 10 +- .../file_system/programs/configurator.dm | 8 +- .../file_system/programs/crewmanifest.dm | 4 +- .../file_system/programs/file_browser.dm | 21 +- .../file_system/programs/jobmanagement.dm | 22 +- .../file_system/programs/ntdownloader.dm | 49 +-- .../file_system/programs/ntmonitor.dm | 6 +- .../file_system/programs/ntnrc_client.dm | 6 +- .../file_system/programs/powermonitor.dm | 3 +- .../file_system/programs/radar.dm | 73 ++--- .../file_system/programs/robocontrol.dm | 18 +- .../file_system/programs/robotact.dm | 147 +++++++++ .../file_system/programs/secureye.dm | 195 ++++++++++++ .../file_system/programs/sm_monitor.dm | 68 ++++- .../modular_computers/hardware/_hardware.dm | 18 +- .../modular_computers/hardware/ai_slot.dm | 6 +- .../modular_computers/hardware/card_slot.dm | 9 +- .../modular_computers/hardware/hard_drive.dm | 43 +-- .../hardware/network_card.dm | 20 ++ .../hardware/portable_disk.dm | 2 +- .../modular_computers/hardware/recharger.dm | 33 ++- .../modular_computers/laptop_vendor.dm | 9 +- tgstation.dme | 3 + 46 files changed, 1311 insertions(+), 318 deletions(-) create mode 100644 code/modules/modular_computers/file_system/programs/budgetordering.dm create mode 100644 code/modules/modular_computers/file_system/programs/robotact.dm create mode 100644 code/modules/modular_computers/file_system/programs/secureye.dm diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index f7a3b46118..79810284cd 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -99,18 +99,18 @@ falloff_exponent = 5 volume = 50 */ -// /datum/looping_sound/computer -// start_sound = 'sound/machines/computer/computer_start.ogg' -// start_length = 7.2 SECONDS -// start_volume = 10 -// mid_sounds = list('sound/machines/computer/computer_mid1.ogg'=1, 'sound/machines/computer/computer_mid2.ogg'=1) -// mid_length = 1.8 SECONDS -// end_sound = 'sound/machines/computer/computer_end.ogg' -// end_volume = 10 -// volume = 2 -// falloff_exponent = 5 //Ultra quiet very fast -// extra_range = -12 -// falloff_distance = 1 //Instant falloff after initial tile +/datum/looping_sound/computer + start_sound = 'sound/machines/computer/computer_start.ogg' + start_length = 7.2 SECONDS + start_volume = 10 + mid_sounds = list('sound/machines/computer/computer_mid1.ogg'=1, 'sound/machines/computer/computer_mid2.ogg'=1) + mid_length = 1.8 SECONDS + end_sound = 'sound/machines/computer/computer_end.ogg' + end_volume = 10 + volume = 2 + falloff_exponent = 5 //Ultra quiet very fast + extra_range = -12 + falloff_distance = 1 //Instant falloff after initial tile // /datum/looping_sound/gravgen // mid_sounds = list('sound/machines/gravgen/gravgen_mid1.ogg'=1,'sound/machines/gravgen/gravgen_mid2.ogg'=1,'sound/machines/gravgen/gravgen_mid3.ogg'=1,'sound/machines/gravgen/gravgen_mid4.ogg'=1,) diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm index 9dde9a0c49..77888fdf01 100644 --- a/code/modules/modular_computers/computers/_modular_computer_shared.dm +++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm @@ -48,8 +48,8 @@ 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/obj/item/card/id/first_ID = card_slot?.stored_card + var/obj/item/card/id/second_ID = card_slot2?.stored_card var/multiple_cards = istype(first_ID) && istype(second_ID) if(user_is_adjacent) . += "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 ? first_ID : second_ID]"]." diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 63cb1cc5fa..90081ec87e 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -20,6 +20,10 @@ 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) @@ -56,11 +60,14 @@ physical = src comp_light_color = "#FFFFFF" idle_threads = list() + if(looping_sound) + soundloop = new(list(src), enabled) update_icon() /obj/item/modular_computer/Destroy() kill_program(forced = TRUE) STOP_PROCESSING(SSobj, src) + QDEL_NULL(soundloop) for(var/H in all_components) var/obj/item/computer_hardware/CH = all_components[H] if(CH.holder == src) @@ -103,7 +110,6 @@ 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)) - //to_chat(user, "There isn't anywhere you can fit a card into on this computer.") return FALSE var/obj/item/card/inserting_id = inserting_item.RemoveID() @@ -112,7 +118,6 @@ if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id))) return TRUE - //to_chat(user, "This computer doesn't have an open card slot.") return FALSE /obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location) @@ -198,7 +203,7 @@ to_chat(user, "You send an activation signal to \the [src], but it responds with an error code. It must be damaged.") else to_chat(user, "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.") - return + return FALSE // If we have a recharger, enable it automatically. Lets computer without a battery work. var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] @@ -210,24 +215,28 @@ to_chat(user, "You send an activation signal to \the [src], turning it on.") else to_chat(user, "You press the power button and start up \the [src].") + if(looping_sound) + soundloop.start() enabled = 1 update_icon() ui_interact(user) + return TRUE else // Unpowered if(issynth) to_chat(user, "You send an activation signal to \the [src] but it does not respond.") else to_chat(user, "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() +/obj/item/modular_computer/process(delta_time) if(!enabled) // The computer is turned off last_power_usage = 0 - return 0 + return if(obj_integrity <= integrity_failure * max_integrity) shutdown_computer() - return 0 + 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. @@ -239,7 +248,7 @@ if(active_program) if(active_program.program_state != PROGRAM_STATE_KILLED) - active_program.process_tick() + active_program.process_tick(delta_time) active_program.ntnet_status = get_ntnet_status() else active_program = null @@ -247,14 +256,36 @@ for(var/I in idle_threads) var/datum/computer_file/program/P = I if(P.program_state != PROGRAM_STATE_KILLED) - P.process_tick() + P.process_tick(delta_time) P.ntnet_status = get_ntnet_status() else idle_threads.Remove(P) - handle_power() // Handles all computer power interaction + 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 tha 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 + playsound(src, sound, 50, TRUE) + visible_message("The [src] displays a [caller.filedesc] notification: [alerttext]") + var/mob/living/holder = loc + if(istype(holder)) + to_chat(holder, "[icon2html(src)] The [src] displays a [caller.filedesc] notification: [alerttext]") + // 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() @@ -344,13 +375,76 @@ 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(loud) physical.visible_message("\The [src] shuts down.") enabled = 0 update_icon() +/** + * Toggles the computer's flashlight, if it has one. + * + * Called from ui_act(), does as the name implies. + * It is seperated 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) + if(light_on) + set_light(comp_light_luminosity, 1, comp_light_color) + else + set_light(0) + 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 seperated 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) + update_light() + return TRUE + +/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool) + if(!all_components.len) + to_chat(user, "This device doesn't have any 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 = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names) + + if(!choice) + return + + if(!Adjacent(user)) + return + + var/obj/item/computer_hardware/H = find_hardware_by_name(choice) + + if(!H) + return + + uninstall_component(H, user) + return + /obj/item/modular_computer/attackby(obj/item/W as obj, mob/user as mob) + // Check for ID first + if(istype(W, /obj/item/card/id) && InsertID(W)) + return + // Insert items into the components for(var/h in all_components) var/obj/item/computer_hardware/H = all_components[h] @@ -386,31 +480,6 @@ to_chat(user, "You repair \the [src].") return - if(W.tool_behaviour == TOOL_SCREWDRIVER) - if(!all_components.len) - to_chat(user, "This device doesn't have any 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 = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names) - - if(!choice) - return - - if(!Adjacent(user)) - return - - var/obj/item/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. diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm index b5188f43d9..92d4a812a2 100644 --- a/code/modules/modular_computers/computers/item/computer_power.dm +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -5,7 +5,7 @@ var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] - if(recharger && recharger.check_functionality()) + if(recharger?.check_functionality()) if(recharger.use_power(amount)) return TRUE @@ -22,7 +22,7 @@ /obj/item/modular_computer/proc/give_power(amount) var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - if(battery_module && battery_module.battery) + if(battery_module?.battery) return battery_module.battery.give(amount) return 0 @@ -41,10 +41,10 @@ shutdown_computer(0) // Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged -/obj/item/modular_computer/proc/handle_power() +/obj/item/modular_computer/proc/handle_power(delta_time) var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] if(recharger) - recharger.process() + recharger.process(delta_time) var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 4a985b93c1..a9f353bca4 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -7,17 +7,17 @@ if(!enabled) if(ui) ui.close() - return 0 + return if(!use_power()) if(ui) ui.close() - return 0 + return // 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 + return // If we have an active program switch to it now. if(active_program) @@ -37,8 +37,8 @@ if (!ui) ui = new(user, src, "NtosMain") ui.set_autoupdate(TRUE) - ui.open() - ui.send_asset(get_asset_datum(/datum/asset/simple/headers)) + if(ui.open()) + ui.send_asset(get_asset_datum(/datum/asset/simple/headers)) /obj/item/modular_computer/ui_data(mob/user) @@ -47,7 +47,9 @@ data["login"] = list() var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD] + data["cardholder"] = FALSE if(cardholder) + data["cardholder"] = TRUE var/obj/item/card/id/stored_card = cardholder.GetID() if(stored_card) var/stored_name = stored_card.registered_name @@ -74,11 +76,11 @@ data["programs"] = list() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] for(var/datum/computer_file/program/P in hard_drive.stored_files) - var/running = 0 + var/running = FALSE if(P in idle_threads) - running = 1 + running = TRUE - data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running)) + data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running, "icon" = P.program_icon, "alert" = P.alert_pending)) data["has_light"] = has_light data["light_on"] = light_on @@ -88,8 +90,10 @@ // Handles user's GUI input /obj/item/modular_computer/ui_act(action, params) - if(..()) + . = ..() + if(.) return + var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] switch(action) if("PC_exit") @@ -144,6 +148,7 @@ if(P in idle_threads) P.program_state = PROGRAM_STATE_ACTIVE active_program = P + P.alert_pending = FALSE idle_threads.Remove(P) update_icon() return @@ -159,16 +164,12 @@ return if(P.run_program(user)) active_program = P + P.alert_pending = FALSE update_icon() return 1 if("PC_toggle_light") - light_on = !light_on - if(light_on) - set_light(comp_light_luminosity, 1, comp_light_color) - else - set_light(0) - return TRUE + return toggle_flashlight() if("PC_light_color") var/mob/user = usr @@ -180,10 +181,7 @@ if(color_hex2num(new_color) < 200) //Colors too dark are rejected to_chat(user, "That color is too dark! Choose a lighter one.") new_color = null - comp_light_color = new_color - light_color = new_color - update_light() - return TRUE + return set_flashlight_color(new_color) if("PC_Eject_Disk") var/param = params["name"] diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm index ef83140a8f..7616e31aa8 100644 --- a/code/modules/modular_computers/computers/item/laptop.dm +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -17,7 +17,7 @@ // No running around with open laptops in hands. item_flags = SLOWS_WHILE_IN_HAND - screen_on = 0 // Starts closed + screen_on = FALSE // Starts closed var/start_open = TRUE // unless this var is set to 1 var/icon_state_closed = "laptop-closed" var/w_class_open = WEIGHT_CLASS_BULKY @@ -64,17 +64,18 @@ . = ..() if(over_object == usr || over_object == src) try_toggle_open(usr) - else if(istype(over_object, /obj/screen/inventory/hand)) + return + 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 - M.put_in_hand(src, H.held_index) + if(M.stat != CONSCIOUS || M.restrained()) + return + if(!isturf(loc) || !Adjacent(M)) + return + M.put_in_hand(src, H.held_index) -/obj/item/modular_computer/laptop/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - . = ..() +/obj/item/modular_computer/laptop/on_attack_hand(mob/user) if(screen_on && isturf(loc)) return attack_self(user) diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index 0d7b567877..970dc8bd1d 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -43,13 +43,6 @@ /obj/item/modular_computer/processor/relay_qdel() qdel(machinery_computer) -// 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(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE) -// if(!machinery_computer) -// return 0 - -// return machinery_computer.canUseTopic(user, state) - /obj/item/modular_computer/processor/shutdown_computer() if(!machinery_computer) return @@ -59,3 +52,9 @@ /obj/item/modular_computer/processor/attack_ghost(mob/user) ui_interact(user) + +/obj/item/modular_computer/processor/alert_call(datum/computer_file/program/caller, alerttext) + if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) + return + playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) + machinery_computer.visible_message("The [src] displays a [caller.filedesc] notification: [alerttext]") diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index 67e8118c7a..a96dffa9a0 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -14,6 +14,7 @@ slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT has_light = TRUE //LED flashlight! comp_light_luminosity = 2.3 //Same as the PDA + looping_sound = FALSE var/has_variants = TRUE var/finish_color = null @@ -41,6 +42,7 @@ comp_light_luminosity = 6.3 has_variants = FALSE device_theme = "syndicate" + light_color = COLOR_RED /obj/item/modular_computer/tablet/nukeops/emag_act(mob/user) if(!enabled) @@ -48,3 +50,98 @@ return FALSE to_chat(user, "You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\".") return FALSE + +/// Borg Built-in tablet interface +/obj/item/modular_computer/tablet/integrated + name = "modular interface" + icon_state = "tablet-silicon" + has_light = FALSE //tablet light button actually enables/disables the borg lamp + comp_light_luminosity = 0 + has_variants = FALSE + ///Ref to the borg we're installed in. Set by the borg during our creation. + var/mob/living/silicon/robot/borgo + ///Ref to the RoboTact app. Important enough to borgs to deserve a ref. + var/datum/computer_file/program/robotact/robotact + ///IC log that borgs can view in their personal management app + var/list/borglog = list() + +/obj/item/modular_computer/tablet/integrated/Initialize(mapload) + . = ..() + vis_flags |= VIS_INHERIT_ID + borgo = loc + if(!istype(borgo)) + borgo = null + stack_trace("[type] initialized outside of a borg, deleting.") + return INITIALIZE_HINT_QDEL + +/obj/item/modular_computer/tablet/integrated/Destroy() + borgo = null + return ..() + +/obj/item/modular_computer/tablet/integrated/turn_on(mob/user) + if(borgo?.stat != DEAD) + return ..() + return FALSE + +/** + * Returns a ref to the RoboTact app, creating the app if need be. + * + * The RoboTact app is important for borgs, and so should always be available. + * This proc will look for it in the tablet's robotact var, then check the + * hard drive if the robotact var is unset, and finally attempt to create a new + * copy if the hard drive does not contain the app. If the hard drive rejects + * the new copy (such as due to lack of space), the proc will crash with an error. + * RoboTact is supposed to be undeletable, so these will create runtime messages. + */ +/obj/item/modular_computer/tablet/integrated/proc/get_robotact() + if(!borgo) + return null + if(!robotact) + var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] + robotact = hard_drive.find_file_by_name("robotact") + if(!robotact) + stack_trace("Cyborg [borgo] ( [borgo.type] ) was somehow missing their self-manage app in their tablet. A new copy has been created.") + robotact = new(hard_drive) + if(!hard_drive.store_file(robotact)) + qdel(robotact) + robotact = null + CRASH("Cyborg [borgo]'s tablet hard drive rejected recieving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.") + return robotact + +//Makes the light settings reflect the borg's headlamp settings +/obj/item/modular_computer/tablet/integrated/ui_data(mob/user) + . = ..() + .["has_light"] = TRUE + .["light_on"] = borgo?.lamp_enabled + .["comp_light_color"] = borgo?.lamp_color + +//Makes the flashlight button affect the borg rather than the tablet +/obj/item/modular_computer/tablet/integrated/toggle_flashlight() + if(!borgo || QDELETED(borgo)) + return FALSE + borgo.toggle_headlamp() + return TRUE + +//Makes the flashlight color setting affect the borg rather than the tablet +/obj/item/modular_computer/tablet/integrated/set_flashlight_color(color) + if(!borgo || QDELETED(borgo) || !color) + return FALSE + borgo.lamp_color = color + borgo.toggle_headlamp(FALSE, TRUE) + return TRUE + +/obj/item/modular_computer/tablet/integrated/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 + borgo.playsound_local(src, sound, 50, TRUE) + to_chat(borgo, "The [src] displays a [caller.filedesc] notification: [alerttext]") + + +/obj/item/modular_computer/tablet/integrated/syndicate + icon_state = "tablet-silicon-syndicate" + device_theme = "syndicate" + + +/obj/item/modular_computer/tablet/integrated/syndicate/Initialize() + . = ..() + borgo.lamp_color = COLOR_RED //Syndicate likes it red diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index 8ac669d2bf..a812c62bc5 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -29,8 +29,7 @@ install_component(new /obj/item/computer_hardware/card_slot) install_component(new /obj/item/computer_hardware/network_card) install_component(new /obj/item/computer_hardware/printer/mini) - hard_drive.store_file(new /datum/computer_file/program/bounty) - //hard_drive.store_file(new /datum/computer_file/program/shipping) + hard_drive.store_file(new /datum/computer_file/program/shipping) /obj/item/modular_computer/tablet/preset/advanced/atmos/Initialize() //This will be defunct and will be replaced when NtOS PDAs are done . = ..() @@ -38,8 +37,10 @@ /obj/item/modular_computer/tablet/preset/advanced/command/Initialize() . = ..() + var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive") install_component(new /obj/item/computer_hardware/sensorpackage) install_component(new /obj/item/computer_hardware/card_slot/secondary) + hard_drive.store_file(new /datum/computer_file/program/budgetorders) /// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink. /obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() @@ -67,3 +68,11 @@ install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) install_component(new /obj/item/computer_hardware/hard_drive/small/nukeops) install_component(new /obj/item/computer_hardware/network_card) + +//Borg Built-in tablet +/obj/item/modular_computer/tablet/integrated/Initialize() + . = ..() + install_component(new /obj/item/computer_hardware/processor_unit/small) + install_component(new /obj/item/computer_hardware/hard_drive/small/integrated) + install_component(new /obj/item/computer_hardware/recharger/cyborg) + install_component(new /obj/item/computer_hardware/network_card/integrated) diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index 0e6f4d161a..090bf1c7fc 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -48,7 +48,7 @@ cpu.attack_ghost(user) /obj/machinery/modular_computer/emag_act(mob/user) - . = ..() + . = ..() if(!cpu) to_chat(user, "You'd need to turn the [src] on first.") return FALSE @@ -59,7 +59,7 @@ icon_state = icon_state_powered if(!cpu || !cpu.enabled) - if (!(stat & NOPOWER) && (cpu && cpu.use_power())) + if (!(stat & NOPOWER) && (cpu?.use_power())) add_overlay(screen_icon_screensaver) else icon_state = icon_state_unpowered @@ -88,16 +88,16 @@ return ..() // Process currently calls handle_power(), may be expanded in future if more things are added. -/obj/machinery/modular_computer/process() +/obj/machinery/modular_computer/process(delta_time) if(cpu) // Keep names in sync. cpu.name = name - cpu.process() + cpu.process(delta_time) // Used in following function to reduce copypaste /obj/machinery/modular_computer/proc/power_failure(malfunction = 0) var/obj/item/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL] - if(cpu && cpu.enabled) // Shut down the computer + if(cpu?.enabled) // Shut down the computer visible_message("\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.") if(cpu) cpu.shutdown_computer(0) @@ -106,14 +106,18 @@ // 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(cpu && cpu.use_power()) // If MC_CPU still has a power source, PC wouldn't go offline. + if(cpu?.use_power()) // If MC_CPU still has a power source, PC wouldn't go offline. stat &= ~NOPOWER update_icon() return . = ..() +/obj/machinery/modular_computer/screwdriver_act(mob/user, obj/item/tool) + if(cpu) + return cpu.screwdriver_act(user, tool) + /obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/user) - if(cpu && !(flags_1 & NODECONSTRUCT_1)) + if (user.a_intent == INTENT_HELP && cpu && !(flags_1 & NODECONSTRUCT_1)) return cpu.attackby(W, user) return ..() @@ -125,11 +129,11 @@ cpu.ex_act(severity) // switch(severity) // if(EXPLODE_DEVASTATE) - // SSexplosions.highobj += cpu + // SSexplosions.high_mov_atom += cpu // if(EXPLODE_HEAVY) - // SSexplosions.medobj += cpu + // SSexplosions.med_mov_atom += cpu // if(EXPLODE_LIGHT) - // SSexplosions.lowobj += cpu + // SSexplosions.low_mov_atom += cpu ..() // EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm index 5d596f98e4..0e27d81305 100644 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -37,7 +37,7 @@ var/obj/item/computer_hardware/network_card/wired/network_card = new() cpu.install_component(network_card) - cpu.install_component(new /obj/item/computer_hardware/recharger/APC) + cpu.install_component(new /obj/item/computer_hardware/recharger/apc_recharger) cpu.install_component(new /obj/item/computer_hardware/hard_drive/super) // Consoles generally have better HDDs due to lower space limitations var/area/A = get_area(src) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 6d6a48d567..a86405f882 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -33,6 +33,14 @@ var/tgui_id /// 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! var/ui_header = null + /// Font Awesome icon to use as this program's icon in the modular computer main menu. Defaults to a basic program maximize window icon if not overridden. + var/program_icon = "window-maximize-o" + /// Whether this program can send alerts while minimized or closed. Used to show a mute button per program in the file manager + var/alert_able = FALSE + /// Whether the user has muted this program's ability to send alerts. + var/alert_silenced = FALSE + /// Whether to highlight our program in the main screen. Intended for alerts, but loosely available for any need to notify of changed conditions. Think Windows task bar highlighting. Available even if alerts are muted. + var/alert_pending = FALSE /datum/computer_file/program/New(obj/item/modular_computer/comp = null) ..() @@ -68,8 +76,8 @@ if(!(hardware_flag & usage_flags)) if(loud && computer && user) to_chat(user, "\The [computer] flashes a \"Hardware Error - Incompatible software\" warning.") - return 0 - return 1 + return FALSE + return TRUE /datum/computer_file/program/proc/get_signal(specific_action = 0) if(computer) @@ -77,21 +85,21 @@ return 0 // Called by Process() on device that runs us, once every tick. -/datum/computer_file/program/proc/process_tick() - return 1 +/datum/computer_file/program/proc/process_tick(delta_time) + return TRUE /** - *Check if the user can run program. Only humans can operate computer. Automatically called in run_program() - *ID must be inserted into a card slot to be read. If the program is not currently installed (as is the case when - *NT Software Hub is checking available software), a list can be given to be used instead. - *Arguments: - *user is a ref of the mob using the device. - *loud is a bool deciding if this proc should use to_chats - *access_to_check is an access level that will be checked against the ID - *transfer, if TRUE and access_to_check is null, will tell this proc to use the program's transfer_access in place of access_to_check - *access can contain a list of access numbers to check against. If access is not empty, it will be used istead of checking any inserted ID. + *Check if the user can run program. Only humans can operate computer. Automatically called in run_program() + *ID must be inserted into a card slot to be read. If the program is not currently installed (as is the case when + *NT Software Hub is checking available software), a list can be given to be used instead. + *Arguments: + *user is a ref of the mob using the device. + *loud is a bool deciding if this proc should use to_chats + *access_to_check is an access level that will be checked against the ID + *transfer, if TRUE and access_to_check is null, will tell this proc to use the program's transfer_access in place of access_to_check + *access can contain a list of access numbers to check against. If access is not empty, it will be used istead of checking any inserted ID. */ -/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE, var/list/access) +/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE, list/access) // Defaults to required_access if(!access_to_check) if(transfer && transfer_access) @@ -147,19 +155,19 @@ ID = card_holder.GetID() generate_network_log("Connection opened -- Program ID: [filename] User:[ID?"[ID.registered_name]":"None"]") program_state = PROGRAM_STATE_ACTIVE - return 1 - return 0 + return TRUE + return FALSE /** - * - *Called by the device when it is emagged. - * - *Emagging the device allows certain programs to unlock new functions. However, the program will - *need to be downloaded first, and then handle the unlock on their own in their run_emag() proc. - *The device will allow an emag to be run multiple times, so the user can re-emag to run the - *override again, should they download something new. The run_emag() proc should return TRUE if - *the emagging affected anything, and FALSE if no change was made (already emagged, or has no - *emag functions). + * + *Called by the device when it is emagged. + * + *Emagging the device allows certain programs to unlock new functions. However, the program will + *need to be downloaded first, and then handle the unlock on their own in their run_emag() proc. + *The device will allow an emag to be run multiple times, so the user can re-emag to run the + *override again, should they download something new. The run_emag() proc should return TRUE if + *the emagging affected anything, and FALSE if no change was made (already emagged, or has no + *emag functions). **/ /datum/computer_file/program/proc/run_emag() return FALSE @@ -179,8 +187,8 @@ ui = SStgui.try_update_ui(user, src, ui) if(!ui && tgui_id) ui = new(user, src, tgui_id, filedesc) - ui.open() - ui.send_asset(get_asset_datum(/datum/asset/simple/headers)) + if(ui.open()) + ui.send_asset(get_asset_datum(/datum/asset/simple/headers)) // CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: // Topic calls are automagically forwarded from NanoModule this program contains. @@ -188,18 +196,20 @@ // Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program) // ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. /datum/computer_file/program/ui_act(action,list/params,datum/tgui/ui) - if(..()) - return 1 + . = ..() + if(.) + return + if(computer) switch(action) if("PC_exit") computer.kill_program() ui.close() - return 1 + return TRUE if("PC_shutdown") computer.shutdown_computer() ui.close() - return 1 + return TRUE if("PC_minimize") var/mob/user = usr if(!computer.active_program || !computer.all_components[MC_CPU]) diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm index 7ae6dd203a..faf2831ca1 100644 --- a/code/modules/modular_computers/file_system/programs/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -9,6 +9,7 @@ transfer_access = ACCESS_HEADS available_on_ntnet = TRUE tgui_id = "NtosAiRestorer" + program_icon = "laptop-code" /// Variable dictating if we are in the process of restoring the AI in the inserted intellicard var/restoring = FALSE @@ -19,7 +20,7 @@ if(computer) ai_slot = computer.all_components[MC_AI] - if(computer && ai_slot && ai_slot.check_functionality()) + if(computer && ai_slot?.check_functionality()) if(cardcheck == 1) return ai_slot if(ai_slot.enabled && ai_slot.stored_card) @@ -31,7 +32,8 @@ return /datum/computer_file/program/aidiag/ui_act(action, params) - if(..()) + . = ..() + if(.) return var/mob/living/silicon/ai/A = get_ai() @@ -47,7 +49,7 @@ if("PRG_eject") if(computer.all_components[MC_AI]) var/obj/item/computer_hardware/ai_slot/ai_slot = computer.all_components[MC_AI] - if(ai_slot && ai_slot.stored_card) + if(ai_slot?.stored_card) ai_slot.try_eject(usr) return TRUE @@ -72,10 +74,10 @@ restoring = FALSE return ai_slot.locked = TRUE - A.adjustOxyLoss(-5, 0)//, FALSE) - A.adjustFireLoss(-5, 0)//, FALSE) - A.adjustToxLoss(-5, 0) - A.adjustBruteLoss(-5, 0) + A.adjustOxyLoss(-5, FALSE) + A.adjustFireLoss(-5, FALSE) + A.adjustToxLoss(-5, FALSE) + A.adjustBruteLoss(-5, FALSE) // Please don't forget to update health, otherwise the below if statements will probably always fail. A.updatehealth() diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index 55dea600e3..646d9892ba 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -7,6 +7,7 @@ requires_ntnet = 1 size = 5 tgui_id = "NtosStationAlertConsole" + program_icon = "bell" var/has_alert = 0 var/alarms = list("Fire" = list(), "Atmosphere" = list(), "Power" = list()) diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm index aa361d4544..8709526de6 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm @@ -9,6 +9,7 @@ unsendable = 1 undeletable = 1 tgui_id = "SyndContractor" + program_icon = "tasks" var/error = "" var/info_screen = TRUE var/assigned = FALSE @@ -18,8 +19,9 @@ . = ..(user) /datum/computer_file/program/contract_uplink/ui_act(action, params) - if(..()) - return TRUE + . = ..() + if(.) + return var/mob/living/user = usr var/obj/item/computer_hardware/hard_drive/small/syndicate/hard_drive = computer.all_components[MC_HDD] 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 803dadc0a0..bb3c62cac2 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -8,6 +8,7 @@ available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosNetDos" + program_icon = "satellite-dish" var/obj/machinery/ntnet_relay/target = null var/dos_speed = 0 @@ -39,7 +40,8 @@ ..() /datum/computer_file/program/ntnet_dos/ui_act(action, params) - if(..()) + . = ..() + if(.) return switch(action) if("PRG_target_relay") 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 4f1c488b9e..ba24a5ab3e 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -8,6 +8,7 @@ available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosRevelation" + program_icon = "magnet" var/armed = 0 /datum/computer_file/program/revelation/run_program(mob/living/user) @@ -17,6 +18,12 @@ /datum/computer_file/program/revelation/proc/activate() if(computer) + if(istype(computer, /obj/item/modular_computer/tablet/integrated)) //If this is a borg's integrated tablet + var/obj/item/modular_computer/tablet/integrated/modularInterface = computer + to_chat(modularInterface.borgo,"SYSTEM PURGE DETECTED/") + addtimer(CALLBACK(modularInterface.borgo, /mob/living/silicon/robot/.proc/death), 2 SECONDS, TIMER_UNIQUE) + return + computer.visible_message("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.") computer.enabled = FALSE computer.update_icon() @@ -39,7 +46,8 @@ /datum/computer_file/program/revelation/ui_act(action, params) - if(..()) + . = ..() + if(.) return switch(action) if("PRG_arm") diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm index 002cf20801..c330cdcbe8 100644 --- a/code/modules/modular_computers/file_system/programs/arcade.dm +++ b/code/modules/modular_computers/file_system/programs/arcade.dm @@ -6,6 +6,7 @@ requires_ntnet = FALSE size = 6 tgui_id = "NtosArcade" + program_icon = "gamepad" ///Returns TRUE if the game is being played. var/game_active = TRUE @@ -27,7 +28,7 @@ // user?.mind?.adjust_experience(/datum/skill/gaming, 1) if(boss_hp <= 0) heads_up = "You have crushed [boss_name]! Rejoice!" - playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/win.ogg', 50) game_active = FALSE program_icon_state = "arcade_off" if(istype(computer)) @@ -37,7 +38,7 @@ sleep(10) else if(player_hp <= 0 || player_mp <= 0) heads_up = "You have been defeated... how will the station survive?" - playsound(computer.loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/lose.ogg', 50) game_active = FALSE program_icon_state = "arcade_off" if(istype(computer)) @@ -57,17 +58,17 @@ return if (boss_mp <= 5) heads_up = "[boss_mpamt] magic power has been stolen from you!" - playsound(computer.loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/steal.ogg', 50, TRUE) player_mp -= boss_mpamt boss_mp += boss_mpamt else if(boss_mp > 5 && boss_hp <12) heads_up = "[boss_name] heals for [bossheal] health!" - playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE) boss_hp += bossheal boss_mp -= boss_mpamt else heads_up = "[boss_name] attacks you for [boss_attackamt] damage!" - playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE) player_hp -= boss_attackamt pause_state = FALSE @@ -91,22 +92,27 @@ return data /datum/computer_file/program/arcade/ui_act(action, list/params) - if(..()) - return TRUE + . = ..() + if(.) + return + var/obj/item/computer_hardware/printer/printer if(computer) printer = computer.all_components[MC_PRINT] - // var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming) - // var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) + // var/gamerSkillLevel = 0 + var/gamerSkill = 0 + // if(usr?.mind) + // gamerSkillLevel = usr.mind.get_skill_level(/datum/skill/gaming) + // gamerSkill = usr.mind.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) switch(action) if("Attack") var/attackamt = 0 //Spam prevention. if(pause_state == FALSE) - attackamt = rand(2,6)// + rand(0, gamerSkill) + attackamt = rand(2,6) + rand(0, gamerSkill) pause_state = TRUE heads_up = "You attack for [attackamt] damage." - playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE) boss_hp -= attackamt sleep(10) game_check() @@ -116,14 +122,14 @@ var/healamt = 0 //More Spam Prevention. var/healcost = 0 if(pause_state == FALSE) - healamt = rand(6,8)// + rand(0, gamerSkill) + healamt = rand(6,8) + rand(0, gamerSkill) var/maxPointCost = 3 // if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN) // maxPointCost = 2 healcost = rand(1, maxPointCost) pause_state = TRUE heads_up = "You heal for [healamt] damage." - playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE) player_hp += healamt player_mp -= healcost sleep(10) @@ -133,10 +139,10 @@ if("Recharge_Power") var/rechargeamt = 0 //As above. if(pause_state == FALSE) - rechargeamt = rand(4,7)// + rand(0, gamerSkill) + rechargeamt = rand(4,7) + rand(0, gamerSkill) pause_state = TRUE heads_up = "You regain [rechargeamt] magic power." - playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3) + playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE) player_mp += rechargeamt sleep(10) game_check() @@ -153,7 +159,7 @@ computer.visible_message("\The [computer] prints out paper.") if(ticket_count >= 1) new /obj/item/stack/arcadeticket((get_turf(computer)), 1) - to_chat(usr, "[computer] dispenses a ticket!") + to_chat(usr, "[src] dispenses a ticket!") ticket_count -= 1 printer.stored_paper -= 1 else diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm index c4b9951838..7c491712fe 100644 --- a/code/modules/modular_computers/file_system/programs/atmosscan.dm +++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm @@ -5,6 +5,7 @@ extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device." size = 4 tgui_id = "NtosAtmos" + program_icon = "thermometer-half" /datum/computer_file/program/atmosscan/run_program(mob/living/user) . = ..() @@ -39,5 +40,6 @@ return data /datum/computer_file/program/atmosscan/ui_act(action, list/params) - if(..()) - return TRUE + . = ..() + if(.) + return diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm index 13caab27ef..46e1f89ee4 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -8,6 +8,7 @@ transfer_access = ACCESS_ROBOTICS size = 5 tgui_id = "NtosCyborgRemoteMonitor" + program_icon = "project-diagram" /datum/computer_file/program/borg_monitor/ui_data(mob/user) var/list/data = get_header_data() @@ -43,7 +44,8 @@ return data /datum/computer_file/program/borg_monitor/ui_act(action, params) - if(..()) + . = ..() + if(.) return switch(action) @@ -54,10 +56,14 @@ var/ID = checkID() if(!ID) return + if(R.stat == DEAD) //Dead borgs will listen to you no longer + to_chat(usr, "Error -- Could not open a connection to unit:[R]") var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message") if(!message) return to_chat(R, "

Message from [ID] -- \"[message]\"
") + to_chat(usr, "Message sent to [R]: [message]") + R.logevent("Message from [ID] -- \"[message]\"") SEND_SOUND(R, 'sound/machines/twobeep_high.ogg') if(R.connected_ai) to_chat(R.connected_ai, "

Message from [ID] to [R] -- \"[message]\"
") diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm index 2e7d3cc87f..9c42a28a9b 100644 --- a/code/modules/modular_computers/file_system/programs/bounty_board.dm +++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm @@ -44,7 +44,8 @@ return data /datum/computer_file/program/bounty_board/ui_act(action, list/params) - if(..()) + . = ..() + if(.) return var/current_ref_num = params["request"] var/current_app_num = params["applicant"] diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm new file mode 100644 index 0000000000..0c7dc9c394 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm @@ -0,0 +1,280 @@ +/datum/computer_file/program/budgetorders + filename = "orderapp" + filedesc = "Nanotrasen Internal Requisition Network (NIRN)" + program_icon_state = "request" + extended_desc = "A request network that utilizes the Nanotrasen Ordering network to purchase supplies using a department budget account." + requires_ntnet = TRUE + transfer_access = ACCESS_HEADS + usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET + size = 20 + tgui_id = "NtosCargo" + ///Are you actually placing orders with it? + var/requestonly = TRUE + ///Can the tablet see or buy illegal stuff? + var/contraband = FALSE + ///Is it being bought from a personal account, or is it being done via a budget/cargo? + var/self_paid = FALSE + ///Can this console approve purchase requests? + var/can_approve_requests = FALSE + ///What do we say when the shuttle moves with living beings on it. + var/safety_warning = "For safety reasons, the automated supply shuttle \ + cannot transport live organisms, human remains, classified nuclear weaponry, \ + homing beacons or machinery housing any form of artificial intelligence." + ///If you're being raided by pirates, what do you tell the crew? + var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible." + +/datum/computer_file/program/budgetorders/proc/get_export_categories() + . = EXPORT_CARGO + +/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, list/access, contraband) + if(issilicon(user)) //Borgs can't buy things. + return FALSE + if(computer.obj_flags & EMAGGED) + return TRUE + else if(contraband) //Hide contrband when non-emagged. + return FALSE + if(!paccess_to_check) // No required_access, allow it. + return TRUE + if(isAdminGhostAI(user)) + return TRUE + + //Aquire access from the inserted ID card. + if(!length(access)) + var/obj/item/card/id/D + var/obj/item/computer_hardware/card_slot/card_slot + if(computer) + card_slot = computer.all_components[MC_CARD] + D = card_slot?.GetID() + if(!D) + return FALSE + access = D.GetAccess() + + if(paccess_to_check in access) + return TRUE + + return FALSE + +/datum/computer_file/program/budgetorders/ui_data() + . = ..() + var/list/data = get_header_data() + data["location"] = SSshuttle.supply.getStatusText() + var/datum/bank_account/buyer = SSeconomy.get_dep_account(ACCOUNT_CAR) + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + var/obj/item/card/id/id_card = card_slot?.GetID() + if(id_card?.registered_account) + if(ACCESS_HEADS in id_card.access) + requestonly = FALSE + buyer = SSeconomy.get_dep_account(id_card.registered_account.account_job.paycheck_department) + can_approve_requests = TRUE + else + requestonly = TRUE + can_approve_requests = FALSE + else + requestonly = TRUE + if(buyer) + data["points"] = buyer.account_balance + +//Otherwise static data, that is being applied in ui_data as the crates visible and buyable are not static, and are determined by inserted ID. + data["requestonly"] = requestonly + data["supplies"] = list() + for(var/pack in SSshuttle.supply_packs) + var/datum/supply_pack/P = SSshuttle.supply_packs[pack] + if(!is_visible_pack(usr, P.access_view , null, P.contraband) || P.hidden) + continue + if(!data["supplies"][P.group]) + data["supplies"][P.group] = list( + "name" = P.group, + "packs" = list() + ) + if((P.hidden && (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly)) + continue + data["supplies"][P.group]["packs"] += list(list( + "name" = P.name, + "cost" = P.cost, + "id" = pack, + "desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name. + "goody" = P.goody, + "access" = P.access + )) + +//Data regarding the User's capability to buy things. + data["has_id"] = id_card + data["away"] = SSshuttle.supply.getDockedId() == "supply_away" + data["self_paid"] = self_paid + data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE + data["loan"] = !!SSshuttle.shuttle_loan + data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched + data["can_send"] = FALSE //There is no situation where I want the app to be able to send the shuttle AWAY from the station, but conversely is fine. + data["can_approve_requests"] = can_approve_requests + data["app_cost"] = TRUE + var/message = "Remember to stamp and send back the supply manifests." + if(SSshuttle.centcom_message) + message = SSshuttle.centcom_message + if(SSshuttle.supplyBlocked) + message = blockade_warning + data["message"] = message + data["cart"] = list() + for(var/datum/supply_order/SO in SSshuttle.shoppinglist) + data["cart"] += list(list( + "object" = SO.pack.name, + "cost" = SO.pack.cost, + "id" = SO.id, + "orderer" = SO.orderer, + "paid" = !isnull(SO.paying_account) //paid by requester + )) + + data["requests"] = list() + for(var/datum/supply_order/SO in SSshuttle.requestlist) + data["requests"] += list(list( + "object" = SO.pack.name, + "cost" = SO.pack.cost, + "orderer" = SO.orderer, + "reason" = SO.reason, + "id" = SO.id + )) + + return data + +/datum/computer_file/program/budgetorders/ui_act(action, params, datum/tgui/ui) + if(..()) + return + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + switch(action) + if("send") + if(!SSshuttle.supply.canMove()) + computer.say(safety_warning) + return + if(SSshuttle.supplyBlocked) + computer.say(blockade_warning) + return + if(SSshuttle.supply.getDockedId() == "supply_home") + SSshuttle.supply.export_categories = get_export_categories() + SSshuttle.moveShuttle("supply", "supply_away", TRUE) + computer.say("The supply shuttle is departing.") + computer.investigate_log("[key_name(usr)] sent the supply shuttle away.", INVESTIGATE_CARGO) + else + computer.investigate_log("[key_name(usr)] called the supply shuttle.", INVESTIGATE_CARGO) + computer.say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minutes.") + SSshuttle.moveShuttle("supply", "supply_home", TRUE) + . = TRUE + if("loan") + if(!SSshuttle.shuttle_loan) + return + if(SSshuttle.supplyBlocked) + computer.say(blockade_warning) + return + else if(SSshuttle.supply.mode != SHUTTLE_IDLE) + return + else if(SSshuttle.supply.getDockedId() != "supply_away") + return + else + SSshuttle.shuttle_loan.loan_shuttle() + computer.say("The supply shuttle has been loaned to CentCom.") + computer.investigate_log("[key_name(usr)] accepted a shuttle loan event.", INVESTIGATE_CARGO) + log_game("[key_name(usr)] accepted a shuttle loan event.") + . = TRUE + if("add") + var/id = text2path(params["id"]) + var/datum/supply_pack/pack = SSshuttle.supply_packs[id] + if(!istype(pack)) + return + if((pack.hidden && (pack.contraband && !contraband) || pack.DropPodOnly)) + return + + var/name = "*None Provided*" + var/rank = "*None Provided*" + var/ckey = usr.ckey + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + name = H.get_authentification_name() + rank = H.get_assignment(hand_first = TRUE) + else if(issilicon(usr)) + name = usr.real_name + rank = "Silicon" + + var/datum/bank_account/account + if(self_paid) + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/id_card = H.get_idcard(TRUE) + if(!istype(id_card)) + computer.say("No ID card detected.") + return + if(istype(id_card, /obj/item/card/id/departmental_budget)) + computer.say("The [src] rejects [id_card].") + return + account = id_card.registered_account + if(!istype(account)) + computer.say("Invalid bank account.") + return + + var/reason = "" + if((requestonly && !self_paid) || !(card_slot?.GetID())) + reason = stripped_input("Reason:", name, "") + if(isnull(reason) || ..()) + return + + if(pack.goody && !self_paid) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE) + computer.say("ERROR: Small crates may only be purchased by private accounts.") + return + + if(!self_paid && ishuman(usr) && !account) + var/obj/item/card/id/id_card = card_slot?.GetID() + account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department) + + var/turf/T = get_turf(src) + var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account) + SO.generateRequisition(T) + if((requestonly && !self_paid) || !(card_slot?.GetID())) + SSshuttle.requestlist += SO + else + SSshuttle.shoppinglist += SO + if(self_paid) + computer.say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.") + . = TRUE + if("remove") + var/id = text2num(params["id"]) + for(var/datum/supply_order/SO in SSshuttle.shoppinglist) + if(SO.id == id) + SSshuttle.shoppinglist -= SO + . = TRUE + break + if("clear") + SSshuttle.shoppinglist.Cut() + . = TRUE + if("approve") + var/id = text2num(params["id"]) + for(var/datum/supply_order/SO in SSshuttle.requestlist) + if(SO.id == id) + var/obj/item/card/id/id_card = card_slot?.GetID() + if(id_card && id_card?.registered_account) + SO.paying_account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department) + SSshuttle.requestlist -= SO + SSshuttle.shoppinglist += SO + . = TRUE + break + if("deny") + var/id = text2num(params["id"]) + for(var/datum/supply_order/SO in SSshuttle.requestlist) + if(SO.id == id) + SSshuttle.requestlist -= SO + . = TRUE + break + if("denyall") + SSshuttle.requestlist.Cut() + . = TRUE + if("toggleprivate") + self_paid = !self_paid + . = TRUE + if(.) + post_signal("supply") + +/datum/computer_file/program/budgetorders/proc/post_signal(command) + + var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) + + if(!frequency) + return + + var/datum/signal/status_signal = new(list("command" = command)) + frequency.post_signal(src, status_signal) diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 3455547d20..65bb5f2343 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -15,6 +15,7 @@ requires_ntnet = 0 size = 8 tgui_id = "NtosCard" + program_icon = "id-card" var/is_centcom = FALSE var/minor = FALSE @@ -94,8 +95,9 @@ return FALSE /datum/computer_file/program/card_mod/ui_act(action, params) - if(..()) - return TRUE + . = ..() + if(.) + return var/obj/item/computer_hardware/card_slot/card_slot var/obj/item/computer_hardware/card_slot/card_slot2 @@ -130,7 +132,7 @@ if(!authenticated) return var/contents = {"

Access Report

- Prepared By: [user_id_card && user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
+ Prepared By: [user_id_card?.registered_name ? user_id_card.registered_name : "Unknown"]
For: [target_id_card.registered_name ? target_id_card.registered_name : "Unregistered"]

Assignment: [target_id_card.assignment]
@@ -175,7 +177,7 @@ if("PRG_edit") if(!computer || !authenticated || !target_id_card) return - var/new_name = reject_bad_name(params["name"]) + var/new_name = params["name"] if(!new_name) return target_id_card.registered_name = new_name @@ -190,7 +192,7 @@ return if(target == "Custom") - var/custom_name = reject_bad_name(params["custom_name"]) + var/custom_name = params["custom_name"] if(custom_name) target_id_card.assignment = custom_name target_id_card.update_label() @@ -320,32 +322,31 @@ /datum/computer_file/program/card_mod/ui_data(mob/user) var/list/data = get_header_data() + data["station_name"] = station_name() + var/obj/item/computer_hardware/card_slot/card_slot2 var/obj/item/computer_hardware/printer/printer if(computer) card_slot2 = computer.all_components[MC_CARD2] printer = computer.all_components[MC_PRINT] - - data["station_name"] = station_name() - - if(computer) data["have_id_slot"] = !!(card_slot2) - data["have_printer"] = !!printer + data["have_printer"] = !!(printer) else data["have_id_slot"] = FALSE data["have_printer"] = FALSE data["authenticated"] = authenticated + if(!card_slot2) + return data //We're just gonna error out on the js side at this point anyway - if(computer) - var/obj/item/card/id/id_card = card_slot2.stored_card - data["has_id"] = !!id_card - data["id_name"] = id_card ? id_card.name : "-----" - if(id_card) - data["id_rank"] = id_card.assignment ? id_card.assignment : "Unassigned" - data["id_owner"] = id_card.registered_name ? id_card.registered_name : "-----" - data["access_on_card"] = id_card.access + var/obj/item/card/id/id_card = card_slot2.stored_card + data["has_id"] = !!id_card + data["id_name"] = id_card ? id_card.name : "-----" + if(id_card) + data["id_rank"] = id_card.assignment ? id_card.assignment : "Unassigned" + data["id_owner"] = id_card.registered_name ? id_card.registered_name : "-----" + data["access_on_card"] = id_card.access return data diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm index db8d6d9f82..89a3b3247d 100644 --- a/code/modules/modular_computers/file_system/programs/cargoship.dm +++ b/code/modules/modular_computers/file_system/programs/cargoship.dm @@ -5,6 +5,7 @@ extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping." size = 6 tgui_id = "NtosShipping" + program_icon = "tags" ///Account used for creating barcodes. var/datum/bank_account/payments_acc ///The amount which the tagger will receive for the sale. @@ -19,14 +20,15 @@ data["has_id_slot"] = !!card_slot data["has_printer"] = !!printer data["paperamt"] = printer ? "[printer.stored_paper] / [printer.max_paper]" : null - data["card_owner"] = card_slot && card_slot.stored_card ? id_card.registered_name : "No Card Inserted." + data["card_owner"] = card_slot?.stored_card ? id_card.registered_name : "No Card Inserted." data["current_user"] = payments_acc ? payments_acc.account_holder : null data["barcode_split"] = percent_cut return data /datum/computer_file/program/shipping/ui_act(action, list/params) - if(..()) - return TRUE + . = ..() + if(.) + return if(!computer) return @@ -40,7 +42,7 @@ switch(action) if("ejectid") if(id_card) - card_slot.try_eject(TRUE, usr) + card_slot.try_eject(usr, TRUE) if("selectid") if(!id_card) return diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm index fae06544d5..cf5d950c0f 100644 --- a/code/modules/modular_computers/file_system/programs/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/configurator.dm @@ -13,6 +13,7 @@ available_on_ntnet = 0 requires_ntnet = 0 tgui_id = "NtosConfiguration" + program_icon = "cog" var/obj/item/modular_computer/movable = null @@ -34,11 +35,11 @@ data["disk_used"] = hard_drive.used_capacity data["power_usage"] = movable.last_power_usage data["battery_exists"] = battery_module ? 1 : 0 - if(battery_module && battery_module.battery) + if(battery_module?.battery) data["battery_rating"] = battery_module.battery.maxcharge data["battery_percent"] = round(battery_module.battery.percent()) - if(battery_module && battery_module.battery) + if(battery_module?.battery) data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge)) var/list/all_entries[0] @@ -57,7 +58,8 @@ /datum/computer_file/program/computerconfig/ui_act(action,params) - if(..()) + . = ..() + if(.) return switch(action) if("PC_toggle_component") diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm index 4f2688d8f1..debe87259d 100644 --- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm +++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm @@ -7,6 +7,7 @@ requires_ntnet = TRUE size = 4 tgui_id = "NtosCrewManifest" + program_icon = "clipboard-list" /datum/computer_file/program/crew_manifest/ui_static_data(mob/user) var/list/data = list() @@ -27,7 +28,8 @@ return data /datum/computer_file/program/crew_manifest/ui_act(action, params, datum/tgui/ui) - if(..()) + . = ..() + if(.) return var/obj/item/computer_hardware/printer/printer diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/file_browser.dm index aba826fce8..97a71496ea 100644 --- a/code/modules/modular_computers/file_system/programs/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/file_browser.dm @@ -8,12 +8,14 @@ available_on_ntnet = FALSE undeletable = TRUE tgui_id = "NtosFileManager" + program_icon = "folder" var/open_file var/error /datum/computer_file/program/filemanager/ui_act(action, params) - if(..()) + . = ..() + if(.) return var/obj/item/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] @@ -65,6 +67,13 @@ var/datum/computer_file/C = F.clone(FALSE) HDD.store_file(C) return TRUE + if("PRG_togglesilence") + if(!HDD) + return + var/datum/computer_file/program/binary = HDD.find_file_by_name(params["name"]) + if(!binary || !istype(binary)) + return + binary.alert_silenced = !binary.alert_silenced /datum/computer_file/program/filemanager/ui_data(mob/user) var/list/data = get_header_data() @@ -78,11 +87,19 @@ else var/list/files = list() for(var/datum/computer_file/F in HDD.stored_files) + var/noisy = FALSE + var/silenced = FALSE + var/datum/computer_file/program/binary = F + if(istype(binary)) + noisy = binary.alert_able + silenced = binary.alert_silenced files += list(list( "name" = F.filename, "type" = F.filetype, "size" = F.size, - "undeletable" = F.undeletable + "undeletable" = F.undeletable, + "alert_able" = noisy, + "alert_silenced" = silenced )) data["files"] = files if(RHDD) diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm index b88b793b66..3f21d2cf2c 100644 --- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm +++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm @@ -7,6 +7,7 @@ requires_ntnet = TRUE size = 4 tgui_id = "NtosJobManager" + program_icon = "address-book" var/change_position_cooldown = 30 //Jobs you cannot open new positions for @@ -49,17 +50,14 @@ return FALSE /datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui) - if(..()) + . = ..() + if(.) return - var/authed = FALSE - var/mob/user = usr - var/obj/item/card/id/user_id = user.get_idcard() - if(user_id) - if(ACCESS_CHANGE_IDS in user_id.access) - authed = TRUE + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + var/obj/item/card/id/user_id = card_slot?.stored_card - if(!authed) + if(!user_id || !(ACCESS_CHANGE_IDS in user_id.access)) return switch(action) @@ -107,10 +105,10 @@ var/list/data = get_header_data() var/authed = FALSE - var/obj/item/card/id/user_id = user.get_idcard(FALSE) - if(user_id) - if(ACCESS_CHANGE_IDS in user_id.access) - authed = TRUE + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + var/obj/item/card/id/user_id = card_slot?.stored_card + if(user_id && (ACCESS_CHANGE_IDS in user_id.access)) + authed = TRUE data["authed"] = authed diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index 8fbcfd0b01..f3fa6df2b3 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -11,10 +11,11 @@ available_on_ntnet = FALSE ui_header = "downloader_finished.gif" tgui_id = "NtosNetDownloader" + program_icon = "download" var/datum/computer_file/program/downloaded_file = null - var/hacked_download = 0 - var/download_completion = 0 //GQ of downloaded data. + var/hacked_download = FALSE + var/download_completion = FALSE //GQ of downloaded data. var/download_netspeed = 0 var/downloaderror = "" var/obj/item/modular_computer/my_computer = null @@ -36,33 +37,33 @@ /datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename) if(downloaded_file) - return 0 + return FALSE var/datum/computer_file/program/PRG = SSnetworks.station_network.find_ntnet_file_by_name(filename) if(!PRG || !istype(PRG)) - return 0 + return FALSE // Attempting to download antag only program, but without having emagged/syndicate computer. No. if(PRG.available_on_syndinet && !emagged) - return 0 + return FALSE var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] if(!computer || !hard_drive || !hard_drive.can_store_file(PRG)) - return 0 + return FALSE ui_header = "downloader_running.gif" if(PRG in main_repo) generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.") - hacked_download = 0 + hacked_download = FALSE else if(PRG in antag_repo) generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.") - hacked_download = 1 + hacked_download = TRUE else generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from unspecified server.") - hacked_download = 0 + hacked_download = FALSE downloaded_file = PRG.clone() @@ -71,7 +72,7 @@ return generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : "[downloaded_file.filename].[downloaded_file.filetype]"].") downloaded_file = null - download_completion = 0 + download_completion = FALSE ui_header = "downloader_finished.gif" /datum/computer_file/program/ntnetdownload/proc/complete_file_download() @@ -83,7 +84,7 @@ // The download failed downloaderror = "I/O ERROR - Unable to save file. Check whether you have enough free space on your hard drive and whether your hard drive is properly connected. If the issue persists contact your system administrator for assistance." downloaded_file = null - download_completion = 0 + download_completion = FALSE ui_header = "downloader_finished.gif" /datum/computer_file/program/ntnetdownload/process_tick() @@ -104,21 +105,22 @@ download_completion += download_netspeed /datum/computer_file/program/ntnetdownload/ui_act(action, params) - if(..()) - return 1 + . = ..() + if(.) + return switch(action) if("PRG_downloadfile") if(!downloaded_file) begin_file_download(params["filename"]) - return 1 + return TRUE if("PRG_reseterror") if(downloaderror) - download_completion = 0 - download_netspeed = 0 + download_completion = FALSE + download_netspeed = FALSE downloaded_file = null downloaderror = "" - return 1 - return 0 + return TRUE + return FALSE /datum/computer_file/program/ntnetdownload/ui_data(mob/user) my_computer = computer @@ -148,7 +150,7 @@ for(var/A in main_repo) var/datum/computer_file/program/P = A // Only those programs our user can run will show in the list - if(!P.can_run(user,transfer = 1, access = access) || hard_drive.find_file_by_name(P.filename)) + if(hard_drive.find_file_by_name(P.filename)) continue all_entries.Add(list(list( "filename" = P.filename, @@ -156,6 +158,7 @@ "fileinfo" = P.extended_desc, "compatibility" = check_compatibility(P), "size" = P.size, + "access" = P.can_run(user,transfer = 1, access = access) ))) data["hackedavailable"] = FALSE if(emagged) // If we are running on emagged computer we have access to some "bonus" software @@ -169,7 +172,9 @@ "filename" = P.filename, "filedesc" = P.filedesc, "fileinfo" = P.extended_desc, + "compatibility" = check_compatibility(P), "size" = P.size, + "access" = TRUE, ))) data["hacked_programs"] = hacked_programs @@ -180,13 +185,13 @@ /datum/computer_file/program/ntnetdownload/proc/check_compatibility(datum/computer_file/program/P) var/hardflag = computer.hardware_flag - if(P && P.is_supported_by_hardware(hardflag,0)) + if(P?.is_supported_by_hardware(hardflag,0)) return "Compatible" return "Incompatible!" /datum/computer_file/program/ntnetdownload/kill_program(forced) abort_file_download() - return ..(forced) + return ..() //////////////////////// //Syndicate Downloader// @@ -199,7 +204,7 @@ filedesc = "Software Download Tool" program_icon_state = "generic" extended_desc = "This program allows downloads of software from shared Syndicate repositories" - requires_ntnet = 0 + requires_ntnet = FALSE ui_header = "downloader_finished.gif" tgui_id = "NtosNetDownloader" emagged = TRUE diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm index bbbde14780..63f0b18a74 100644 --- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm @@ -1,6 +1,6 @@ /datum/computer_file/program/ntnetmonitor filename = "wirecarp" - filedesc = "WireCarp" //wireshark. + filedesc = "WireCarp" program_icon_state = "comm_monitor" extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes" size = 12 @@ -8,9 +8,11 @@ required_access = ACCESS_NETWORK //NETWORK CONTROL IS A MORE SECURE PROGRAM. available_on_ntnet = TRUE tgui_id = "NtosNetMonitor" + program_icon = "network-wired" /datum/computer_file/program/ntnetmonitor/ui_act(action, params) - if(..()) + . = ..() + if(.) return switch(action) if("resetIDS") diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index f03ff3f8fd..19172f130a 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -9,6 +9,7 @@ ui_header = "ntnrc_idle.gif" available_on_ntnet = 1 tgui_id = "NtosNetChat" + program_icon = "comment-alt" var/last_message // Used to generate the toolbar icon var/username var/active_channel @@ -20,7 +21,8 @@ username = "DefaultUser[rand(100, 999)]" /datum/computer_file/program/chatclient/ui_act(action, params) - if(..()) + . = ..() + if(.) return var/datum/ntnet_conversation/channel = SSnetworks.station_network.get_chat_channel_by_id(active_channel) @@ -182,7 +184,7 @@ var/list/all_channels = list() for(var/C in SSnetworks.station_network.chat_channels) var/datum/ntnet_conversation/conv = C - if(conv && conv.title) + if(conv?.title) all_channels.Add(list(list( "chan" = conv.title, "id" = conv.id diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm index e87a731a40..78a14ff1ad 100644 --- a/code/modules/modular_computers/file_system/programs/powermonitor.dm +++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm @@ -11,6 +11,7 @@ requires_ntnet = 0 size = 9 tgui_id = "NtosPowerMonitor" + program_icon = "plug" var/has_alert = 0 var/obj/structure/cable/attached_wire @@ -49,7 +50,7 @@ local_apc = null /datum/computer_file/program/power_monitor/proc/get_powernet() //keep in sync with /obj/machinery/computer/monitor's version - if(attached_wire || (local_apc && local_apc.terminal)) + if(attached_wire || (local_apc?.terminal)) return attached_wire ? attached_wire.powernet : local_apc.terminal.powernet return FALSE diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm index 216365d6ea..0bf5eb2118 100644 --- a/code/modules/modular_computers/file_system/programs/radar.dm +++ b/code/modules/modular_computers/file_system/programs/radar.dm @@ -63,7 +63,8 @@ return data /datum/computer_file/program/radar/ui_act(action, params) - if(..()) + . = ..() + if(.) return switch(action) @@ -73,13 +74,13 @@ scan() /** - *Updates tracking information of the selected target. - * - *The track() proc updates the entire set of information about the location - *of the target, including whether the Ntos window should use a pinpointer - *crosshair over the up/down arrows, or none in favor of a rotating arrow - *for far away targets. This information is returned in the form of a list. - * + *Updates tracking information of the selected target. + * + *The track() proc updates the entire set of information about the location + *of the target, including whether the Ntos window should use a pinpointer + *crosshair over the up/down arrows, or none in favor of a rotating arrow + *for far away targets. This information is returned in the form of a list. + * */ /datum/computer_file/program/radar/proc/track() var/atom/movable/signal = find_atom() @@ -115,13 +116,13 @@ return trackinfo /** - * - *Checks the trackability of the selected target. - * - *If the target is on the computer's Z level, or both are on station Z - *levels, and the target isn't untrackable, return TRUE. - *Arguments: - **arg1 is the atom being evaluated. + * + *Checks the trackability of the selected target. + * + *If the target is on the computer's Z level, or both are on station Z + *levels, and the target isn't untrackable, return TRUE. + *Arguments: + **arg1 is the atom being evaluated. */ /datum/computer_file/program/radar/proc/trackable(atom/movable/signal) if(!signal || !computer) @@ -133,30 +134,30 @@ return (there.z == here.z) || (is_station_level(here.z) && is_station_level(there.z)) /** - * - *Runs a scan of all the trackable atoms. - * - *Checks each entry in the GLOB of the specific trackable atoms against - *the track() proc, and fill the objects list with lists containing the - *atoms' names and REFs. The objects list is handed to the tgui screen - *for displaying to, and being selected by, the user. A two second - *sleep is used to delay the scan, both for thematical reasons as well - *as to limit the load players may place on the server using these - *somewhat costly loops. + * + *Runs a scan of all the trackable atoms. + * + *Checks each entry in the GLOB of the specific trackable atoms against + *the track() proc, and fill the objects list with lists containing the + *atoms' names and REFs. The objects list is handed to the tgui screen + *for displaying to, and being selected by, the user. A two second + *sleep is used to delay the scan, both for thematical reasons as well + *as to limit the load players may place on the server using these + *somewhat costly loops. */ /datum/computer_file/program/radar/proc/scan() return /** - * - *Finds the atom in the appropriate list that the `selected` var indicates - * - *The `selected` var holds a REF, which is a string. A mob REF may be - *something like "mob_209". In order to find the actual atom, we need - *to search the appropriate list for the REF string. This is dependant - *on the program (Lifeline uses GLOB.human_list, while Fission360 uses - *GLOB.poi_list), but the result will be the same; evaluate the string and - *return an atom reference. + * + *Finds the atom in the appropriate list that the `selected` var indicates + * + *The `selected` var holds a REF, which is a string. A mob REF may be + *something like "mob_209". In order to find the actual atom, we need + *to search the appropriate list for the REF string. This is dependant + *on the program (Lifeline uses GLOB.human_list, while Fission360 uses + *GLOB.poi_list), but the result will be the same; evaluate the string and + *return an atom reference. */ /datum/computer_file/program/radar/proc/find_atom() return @@ -212,6 +213,7 @@ requires_ntnet = TRUE transfer_access = ACCESS_MEDICAL available_on_ntnet = TRUE + program_icon = "heartbeat" /datum/computer_file/program/radar/lifeline/find_atom() return locate(selected) in GLOB.human_list @@ -228,7 +230,7 @@ var/crewmember_name = "Unknown" if(humanoid.wear_id) var/obj/item/card/id/ID = humanoid.wear_id.GetID() - if(ID && ID.registered_name) + if(ID?.registered_name) crewmember_name = ID.registered_name var/list/crewinfo = list( ref = REF(humanoid), @@ -262,6 +264,7 @@ available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosRadarSyndicate" + program_icon = "bomb" arrowstyle = "ntosradarpointerS.png" pointercolor = "red" diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm index c0b82b9c95..8c41ea6c38 100644 --- a/code/modules/modular_computers/file_system/programs/robocontrol.dm +++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm @@ -1,13 +1,14 @@ /datum/computer_file/program/robocontrol filename = "botkeeper" - filedesc = "Botkeeper" + filedesc = "BotKeeper" program_icon_state = "robot" extended_desc = "A remote controller used for giving basic commands to non-sentient robots." - transfer_access = ACCESS_ROBOTICS + transfer_access = null requires_ntnet = TRUE size = 12 tgui_id = "NtosRoboControl" + program_icon = "robot" ///Number of simple robots on-station. var/botcount = 0 ///Used to find the location of the user for the purposes of summoning robots. @@ -36,7 +37,13 @@ for(var/B in GLOB.bots_list) var/mob/living/simple_animal/bot/Bot = B if(!Bot.on || Bot.z != zlevel || Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected! - continue //Also, the PDA must have access to the bot type. + continue + else if(computer) //Also, the inserted ID must have access to the bot type + var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : null + if(!id_card && !Bot.bot_core.allowed(current_user)) + continue + else if(id_card && !Bot.bot_core.check_access(id_card)) + continue var/list/newbot = list("name" = Bot.name, "mode" = Bot.get_mode_ui(), "model" = Bot.model, "locat" = get_area(Bot), "bot_ref" = REF(Bot), "mule_check" = FALSE) if(Bot.bot_type == MULE_BOT) var/mob/living/simple_animal/bot/mulebot/MULE = Bot @@ -53,8 +60,9 @@ return data /datum/computer_file/program/robocontrol/ui_act(action, list/params) - if(..()) - return TRUE + . = ..() + if(.) + return var/obj/item/computer_hardware/card_slot/card_slot var/obj/item/card/id/id_card if(computer) diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm new file mode 100644 index 0000000000..0bd166624e --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/robotact.dm @@ -0,0 +1,147 @@ +/datum/computer_file/program/robotact + filename = "robotact" + filedesc = "RoboTact" + extended_desc = "A built-in app for cyborg self-management and diagnostics." + ui_header = "robotact.gif" //DEBUG -- new icon before PR + program_icon_state = "command" + requires_ntnet = FALSE + transfer_access = null + available_on_ntnet = FALSE + unsendable = TRUE + undeletable = TRUE + usage_flags = PROGRAM_TABLET + size = 5 + tgui_id = "NtosRobotact" + program_icon = "terminal" + ///A typed reference to the computer, specifying the borg tablet type + var/obj/item/modular_computer/tablet/integrated/tablet + +/datum/computer_file/program/robotact/Destroy() + tablet = null + return ..() + +/datum/computer_file/program/robotact/run_program(mob/living/user) + if(!istype(computer, /obj/item/modular_computer/tablet/integrated)) + to_chat(user, "A warning flashes across \the [computer]: Device Incompatible.") + return FALSE + . = ..() + if(.) + tablet = computer + if(tablet.device_theme == "syndicate") + program_icon_state = "command-syndicate" + return TRUE + return FALSE + +/datum/computer_file/program/robotact/ui_data(mob/user) + var/list/data = get_header_data() + if(!iscyborg(user)) + return data + var/mob/living/silicon/robot/borgo = tablet.borgo + + data["name"] = borgo.name + data["designation"] = borgo.designation //Borgo module type + data["masterAI"] = borgo.connected_ai //Master AI + + var/charge = 0 + var/maxcharge = 1 + if(borgo.cell) + charge = borgo.cell.charge + maxcharge = borgo.cell.maxcharge + data["charge"] = charge //Current cell charge + data["maxcharge"] = maxcharge //Cell max charge + data["integrity"] = ((borgo.health + 100) / 2) //Borgo health, as percentage + data["lampIntensity"] = borgo.lamp_intensity //Borgo lamp power setting + data["sensors"] = "[borgo.sensors_on?"ACTIVE":"DISABLED"]" + data["printerPictures"] = borgo.connected_ai? borgo.connected_ai.aicamera.stored.len : borgo.aicamera.stored.len //Number of pictures taken, synced to AI if available + data["printerToner"] = borgo.toner //amount of toner + data["printerTonerMax"] = borgo.tonermax //It's a variable, might as well use it + data["thrustersInstalled"] = borgo.ionpulse //If we have a thruster uprade + data["thrustersStatus"] = "[borgo.ionpulse_on?"ACTIVE":"DISABLED"]" //Feedback for thruster status + + //DEBUG -- Cover, TRUE for locked + data["cover"] = "[borgo.locked? "LOCKED":"UNLOCKED"]" + //Ability to move. FAULT if lockdown wire is cut, DISABLED if borg locked, ENABLED otherwise + data["locomotion"] = "[borgo.wires.is_cut(WIRE_LOCKDOWN)?"FAULT":"[borgo.lockcharge?"DISABLED":"ENABLED"]"]" + //Module wire. FAULT if cut, NOMINAL otherwise + data["wireModule"] = "[borgo.wires.is_cut(WIRE_RESET_MODULE)?"FAULT":"NOMINAL"]" + //DEBUG -- Camera(net) wire. FAULT if cut (or no cameranet camera), DISABLED if pulse-disabled, NOMINAL otherwise + data["wireCamera"] = "[!borgo.builtInCamera || borgo.wires.is_cut(WIRE_CAMERA)?"FAULT":"[borgo.builtInCamera.can_use()?"NOMINAL":"DISABLED"]"]" + //AI wire. FAULT if wire is cut, CONNECTED if connected to AI, READY otherwise + data["wireAI"] = "[borgo.wires.is_cut(WIRE_AI)?"FAULT":"[borgo.connected_ai?"CONNECTED":"READY"]"]" + //Law sync wire. FAULT if cut, NOMINAL otherwise + data["wireLaw"] = "[borgo.wires.is_cut(WIRE_LAWSYNC)?"FAULT":"NOMINAL"]" + + return data + +/datum/computer_file/program/robotact/ui_static_data(mob/user) + var/list/data = list() + if(!iscyborg(user)) + return data + var/mob/living/silicon/robot/borgo = user + + data["Laws"] = borgo.laws.get_law_list(TRUE, TRUE, FALSE) + data["borgLog"] = tablet.borglog + data["borgUpgrades"] = borgo.upgrades + return data + +/datum/computer_file/program/robotact/ui_act(action, params) + . = ..() + if(.) + return + + var/mob/living/silicon/robot/borgo = tablet.borgo + + switch(action) + if("coverunlock") + if(borgo.locked) + borgo.locked = FALSE + borgo.update_icons() + if(borgo.emagged) + borgo.logevent("ChÃ¥vÃis cover lock has been [borgo.locked ? "engaged" : "released"]") //"The cover interface glitches out for a split second" + else + borgo.logevent("Chassis cover lock has been [borgo.locked ? "engaged" : "released"]") + + if("lawchannel") + borgo.set_autosay() + + if("lawstate") + borgo.checklaws() + + if("alertPower") + if(borgo.stat == CONSCIOUS) + if(!borgo.cell || !borgo.cell.charge) + borgo.visible_message("The power warning light on [borgo] flashes urgently.", \ + "You announce you are operating in low power mode.") + playsound(borgo, 'sound/machines/buzz-two.ogg', 50, FALSE) + + if("toggleSensors") + borgo.toggle_sensors() + + if("viewImage") + if(borgo.connected_ai) + borgo.connected_ai.aicamera?.viewpictures(usr) + else + borgo.aicamera?.viewpictures(usr) + + if("printImage") + var/obj/item/camera/siliconcam/robot_camera/borgcam = borgo.aicamera + borgcam?.borgprint(usr) + + if("toggleThrusters") + borgo.toggle_ionpulse() + + if("lampIntensity") + borgo.lamp_intensity = params["ref"] + borgo.toggle_headlamp(FALSE, TRUE) + +/** + * Forces a full update of the UI, if currently open. + * + * Forces an update that includes refreshing ui_static_data. Called by + * law changes and borg log additions. + */ +/datum/computer_file/program/robotact/proc/force_full_update() + if(tablet) + var/datum/tgui/active_ui = SStgui.get_open_ui(tablet.borgo, src) + if(active_ui) + active_ui.send_full_update() diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm new file mode 100644 index 0000000000..92275b1e8b --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/secureye.dm @@ -0,0 +1,195 @@ +#define DEFAULT_MAP_SIZE 15 + +/datum/computer_file/program/secureye + filename = "secureye" + filedesc = "SecurEye" + ui_header = "borg_mon.gif" + program_icon_state = "generic" + extended_desc = "This program allows access to standard security camera networks." + requires_ntnet = TRUE + transfer_access = ACCESS_SECURITY + usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP + size = 5 + tgui_id = "NtosSecurEye" + program_icon = "eye" + + var/list/network = list("ss13") + var/obj/machinery/camera/active_camera + /// The turf where the camera was last updated. + var/turf/last_camera_turf + var/list/concurrent_users = list() + + // Stuff needed to render the map + var/map_name + var/atom/movable/screen/map_view/cam_screen + /// All the plane masters that need to be applied. + var/list/cam_plane_masters + var/atom/movable/screen/background/cam_background + +/datum/computer_file/program/secureye/New() + . = ..() + // Map name has to start and end with an A-Z character, + // and definitely NOT with a square bracket or even a number. + map_name = "camera_console_[REF(src)]_map" + // Convert networks to lowercase + for(var/i in network) + network -= i + network += lowertext(i) + // Initialize map objects + cam_screen = new + cam_screen.name = "screen" + cam_screen.assigned_map = map_name + cam_screen.del_on_map_removal = FALSE + cam_screen.screen_loc = "[map_name]:1,1" + cam_plane_masters = list() + for(var/plane in subtypesof(/atom/movable/screen/plane_master)) + var/atom/movable/screen/instance = new plane() + instance.assigned_map = map_name + instance.del_on_map_removal = FALSE + instance.screen_loc = "[map_name]:CENTER" + cam_plane_masters += instance + cam_background = new + cam_background.assigned_map = map_name + cam_background.del_on_map_removal = FALSE + +/datum/computer_file/program/secureye/Destroy() + qdel(cam_screen) + QDEL_LIST(cam_plane_masters) + qdel(cam_background) + return ..() + +/datum/computer_file/program/secureye/ui_interact(mob/user, datum/tgui/ui) + // Update UI + ui = SStgui.try_update_ui(user, src, ui) + + // Update the camera, showing static if necessary and updating data if the location has moved. + update_active_camera_screen() + + if(!ui) + var/user_ref = REF(user) + var/is_living = isliving(user) + // Ghosts shouldn't count towards concurrent users, which produces + // an audible terminal_on click. + if(is_living) + concurrent_users += user_ref + // Register map objects + user.client.register_map_obj(cam_screen) + for(var/plane in cam_plane_masters) + user.client.register_map_obj(plane) + user.client.register_map_obj(cam_background) + return ..() + +/datum/computer_file/program/secureye/ui_data() + var/list/data = get_header_data() + data["network"] = network + data["activeCamera"] = null + if(active_camera) + data["activeCamera"] = list( + name = active_camera.c_tag, + status = active_camera.status, + ) + return data + +/datum/computer_file/program/secureye/ui_static_data() + var/list/data = list() + data["mapRef"] = map_name + var/list/cameras = get_available_cameras() + data["cameras"] = list() + for(var/i in cameras) + var/obj/machinery/camera/C = cameras[i] + data["cameras"] += list(list( + name = C.c_tag, + )) + + return data + +/datum/computer_file/program/secureye/ui_act(action, params) + . = ..() + if(.) + return + + if(action == "switch_camera") + var/c_tag = params["name"] + var/list/cameras = get_available_cameras() + var/obj/machinery/camera/selected_camera = cameras[c_tag] + active_camera = selected_camera + playsound(src, get_sfx("terminal_type"), 25, FALSE) + + if(!selected_camera) + return TRUE + + update_active_camera_screen() + + return TRUE + +/datum/computer_file/program/secureye/ui_close(mob/user) + . = ..() + var/user_ref = REF(user) + var/is_living = isliving(user) + // Living creature or not, we remove you anyway. + concurrent_users -= user_ref + // Unregister map objects + user.client.clear_map(map_name) + // Turn off the console + if(length(concurrent_users) == 0 && is_living) + active_camera = null + playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE) + +/datum/computer_file/program/secureye/proc/update_active_camera_screen() + // Show static if can't use the camera + if(!active_camera?.can_use()) + show_camera_static() + return + + var/list/visible_turfs = list() + + // Is this camera located in or attached to a living thing? If so, assume the camera's loc is the living thing. + var/cam_location = isliving(active_camera.loc) ? active_camera.loc : active_camera + + // If we're not forcing an update for some reason and the cameras are in the same location, + // we don't need to update anything. + // Most security cameras will end here as they're not moving. + var/newturf = get_turf(cam_location) + if(last_camera_turf == newturf) + return + + // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs. + last_camera_turf = get_turf(cam_location) + + var/list/visible_things = active_camera.isXRay() ? range(active_camera.view_range, cam_location) : view(active_camera.view_range, cam_location) + + for(var/turf/visible_turf in visible_things) + visible_turfs += visible_turf + + var/list/bbox = get_bbox_of_atoms(visible_turfs) + var/size_x = bbox[3] - bbox[1] + 1 + var/size_y = bbox[4] - bbox[2] + 1 + + cam_screen.vis_contents = visible_turfs + cam_background.icon_state = "clear" + cam_background.fill_rect(1, 1, size_x, size_y) + +/datum/computer_file/program/secureye/proc/show_camera_static() + cam_screen.vis_contents.Cut() + cam_background.icon_state = "scanline2" + cam_background.fill_rect(1, 1, DEFAULT_MAP_SIZE, DEFAULT_MAP_SIZE) + +// Returns the list of cameras accessible from this computer +/datum/computer_file/program/secureye/proc/get_available_cameras() + var/list/L = list() + for (var/obj/machinery/camera/cam in GLOB.cameranet.cameras) + if(!is_station_level(cam.z))//Only show station cameras. + continue + L.Add(cam) + var/list/camlist = list() + for(var/obj/machinery/camera/cam in L) + if(!cam.network) + stack_trace("Camera in a cameranet has no camera network") + continue + if(!(islist(cam.network))) + stack_trace("Camera in a cameranet has a non-list camera network") + continue + var/list/tempnetwork = cam.network & network + if(tempnetwork.len) + camlist["[cam.c_tag]"] = cam + return camlist diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index e4cf590930..7ef2f7416a 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -8,10 +8,16 @@ transfer_access = ACCESS_CONSTRUCTION size = 5 tgui_id = "NtosSupermatterMonitor" + program_icon = "radiation" + alert_able = TRUE var/last_status = SUPERMATTER_INACTIVE var/list/supermatters var/obj/machinery/power/supermatter_crystal/active // Currently selected supermatter crystal. +/datum/computer_file/program/supermatter_monitor/Destroy() + clear_signals() + active = null + return ..() /datum/computer_file/program/supermatter_monitor/process_tick() ..() @@ -25,10 +31,11 @@ /datum/computer_file/program/supermatter_monitor/run_program(mob/living/user) . = ..(user) + if(!(active in GLOB.machines)) + active = null refresh() /datum/computer_file/program/supermatter_monitor/kill_program(forced = FALSE) - active = null supermatters = null ..() @@ -52,6 +59,58 @@ for(var/obj/machinery/power/supermatter_crystal/S in supermatters) . = max(., S.get_status()) +/** + * Sets up the signal listener for Supermatter delaminations. + * + * Unregisters any old listners for SM delams, and then registers one for the SM refered + * to in the `active` variable. This proc is also used with no active SM to simply clear + * the signal and exit. + */ +/datum/computer_file/program/supermatter_monitor/proc/set_signals() + // if(active) + // RegisterSignal(active, COMSIG_SUPERMATTER_DELAM_ALARM, .proc/send_alert, override = TRUE) + // RegisterSignal(active, COMSIG_SUPERMATTER_DELAM_START_ALARM, .proc/send_start_alert, override = TRUE) + +/** + * Removes the signal listener for Supermatter delaminations from the selected supermatter. + * + * Pretty much does what it says. + */ +/datum/computer_file/program/supermatter_monitor/proc/clear_signals() + // if(active) + // UnregisterSignal(active, COMSIG_SUPERMATTER_DELAM_ALARM) + // UnregisterSignal(active, COMSIG_SUPERMATTER_DELAM_START_ALARM) + +/** + * Sends an SM delam alert to the computer. + * + * Triggered by a signal from the selected supermatter, this proc sends a notification + * to the computer if the program is either closed or minimized. We do not send these + * notifications to the comptuer if we're the active program, because engineers fixing + * the supermatter probably don't need constant beeping to distract them. + */ +/datum/computer_file/program/supermatter_monitor/proc/send_alert() + if(!computer.get_ntnet_status()) + return + if(computer.active_program != src) + computer.alert_call(src, "Crystal delamination in progress!") + alert_pending = TRUE + +/** + * Sends an SM delam start alert to the computer. + * + * Triggered by a signal from the selected supermatter at the start of a delamination, + * this proc sends a notification to the computer if this program is the active one. + * We do this so that people carrying a tablet with NT CIMS open but with the NTOS window + * closed will still get one audio alert. This is not sent to computers with the program + * minimized or closed to avoid double-notifications. + */ +/datum/computer_file/program/supermatter_monitor/proc/send_start_alert() + if(!computer.get_ntnet_status()) + return + if(computer.active_program == src) + computer.alert_call(src, "Crystal delamination in progress!") + /datum/computer_file/program/supermatter_monitor/ui_data() var/list/data = get_header_data() @@ -107,11 +166,13 @@ return data /datum/computer_file/program/supermatter_monitor/ui_act(action, params) - if(..()) - return TRUE + . = ..() + if(.) + return switch(action) if("PRG_clear") + clear_signals() active = null return TRUE if("PRG_refresh") @@ -122,4 +183,5 @@ for(var/obj/machinery/power/supermatter_crystal/S in supermatters) if(S.uid == newuid) active = S + set_signals() return TRUE diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm index 81555340b2..850ba76f06 100644 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -24,8 +24,8 @@ /obj/item/computer_hardware/New(obj/L) ..() - pixel_x = rand(-8, 8) - pixel_y = rand(-8, 8) + pixel_x = base_pixel_x + rand(-8, 8) + pixel_y = base_pixel_y + rand(-8, 8) /obj/item/computer_hardware/Destroy() if(holder) @@ -94,12 +94,20 @@ // Called when component is removed from PC. /obj/item/computer_hardware/proc/on_remove(obj/item/modular_computer/M, mob/living/user = null) - try_eject(forced = 1) + try_eject(forced = TRUE) // Called when someone tries to insert something in it - paper in printer, card in card reader, etc. /obj/item/computer_hardware/proc/try_insert(obj/item/I, mob/living/user = null) return FALSE -// Called when someone tries to eject something from it - card from card reader, etc. -/obj/item/computer_hardware/proc/try_eject(slot=0, mob/living/user = null, forced = 0) +/** + * Implement this when your hardware contains an object that the user can eject. + * + * Examples include ejecting cells from battery modules, ejecting an ID card from a card reader + * or ejecting an Intellicard from an AI card slot. + * Arguments: + * * user - The mob requesting the eject. + * * forced - Whether this action should be forced in some way. + */ +/obj/item/computer_hardware/proc/try_eject(mob/living/user = null, forced = FALSE) return FALSE diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index c874d786a0..5d42747308 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -12,7 +12,7 @@ /obj/item/computer_hardware/ai_slot/handle_atom_del(atom/A) if(A == stored_card) - try_eject(0, null, TRUE) + try_eject(forced = TRUE) . = ..() /obj/item/computer_hardware/ai_slot/examine(mob/user) @@ -39,7 +39,7 @@ return TRUE -/obj/item/computer_hardware/ai_slot/try_eject(mob/living/user = null,forced = FALSE) +/obj/item/computer_hardware/ai_slot/try_eject(mob/living/user = null, forced = FALSE) if(!stored_card) to_chat(user, "There is no card in \the [src].") return FALSE @@ -65,5 +65,5 @@ return if(I.tool_behaviour == TOOL_SCREWDRIVER) to_chat(user, "You press down on the manual eject button with \the [I].") - try_eject(,user,1) + try_eject(user, TRUE) return diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index c243bf7db1..9139eee0b0 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -1,5 +1,5 @@ /obj/item/computer_hardware/card_slot - name = "identification card authentication module" // \improper breaks the find_hardware_by_name proc + name = "primary RFID card module" // \improper breaks the find_hardware_by_name proc desc = "A module allowing this computer to read or write data on ID cards. Necessary for some programs to run properly." power_usage = 10 //W icon_state = "card_mini" @@ -14,7 +14,7 @@ . = ..() /obj/item/computer_hardware/card_slot/Destroy() - try_eject() + try_eject(forced = TRUE) return ..() /obj/item/computer_hardware/card_slot/GetAccess() @@ -100,14 +100,16 @@ to_chat(user, "You adjust the connecter to fit into [expansion_hw ? "an expansion bay" : "the primary ID bay"].") /** - *Swaps the card_slot hardware between using the dedicated card slot bay on a computer, and using an expansion bay. + *Swaps the card_slot hardware between using the dedicated card slot bay on a computer, and using an expansion bay. */ /obj/item/computer_hardware/card_slot/proc/swap_slot() expansion_hw = !expansion_hw if(expansion_hw) device_type = MC_CARD2 + name = "secondary RFID card module" else device_type = MC_CARD + name = "primary RFID card module" /obj/item/computer_hardware/card_slot/examine(mob/user) . = ..() @@ -116,5 +118,6 @@ . += "There appears to be something loaded in the card slots." /obj/item/computer_hardware/card_slot/secondary + name = "secondary RFID card module" device_type = MC_CARD2 expansion_hw = TRUE diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index e5c133de20..8debb00c19 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -31,43 +31,43 @@ // Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. /obj/item/computer_hardware/hard_drive/proc/store_file(datum/computer_file/F) if(!F || !istype(F)) - return 0 + return FALSE if(!can_store_file(F)) - return 0 + return FALSE if(!check_functionality()) - return 0 + return FALSE if(!stored_files) - return 0 + return FALSE // This file is already stored. Don't store it again. if(F in stored_files) - return 0 + return FALSE F.holder = src stored_files.Add(F) recalculate_size() - return 1 + return TRUE // Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. /obj/item/computer_hardware/hard_drive/proc/remove_file(datum/computer_file/F) if(!F || !istype(F)) - return 0 + return FALSE if(!stored_files) - return 0 + return FALSE if(!check_functionality()) - return 0 + return FALSE if(F in stored_files) stored_files -= F recalculate_size() - return 1 + return TRUE else - return 0 + return FALSE // Loops through all stored files and recalculates used_capacity of this drive /obj/item/computer_hardware/hard_drive/proc/recalculate_size() @@ -80,24 +80,24 @@ // Checks whether file can be stored on the hard drive. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file. /obj/item/computer_hardware/hard_drive/proc/can_store_file(datum/computer_file/F) if(!F || !istype(F)) - return 0 + return FALSE if(F in stored_files) - return 0 + return FALSE var/name = F.filename + "." + F.filetype for(var/datum/computer_file/file in stored_files) if((file.filename + "." + file.filetype) == name) - return 0 + return FALSE // In the unlikely event someone manages to create that many files. // BYOND is acting weird with numbers above 999 in loops (infinite loop prevention) if(stored_files.len >= 999) - return 0 + return FALSE if((used_capacity + F.size) > max_capacity) - return 0 + return FALSE else - return 1 + return TRUE // Tries to find the file by filename. Returns null on failure @@ -157,7 +157,14 @@ max_capacity = 64 icon_state = "ssd_mini" w_class = WEIGHT_CLASS_TINY - custom_price = 150 + custom_price = PAYCHECK_MEDIUM * 2 + +// For borg integrated tablets. No downloader. +/obj/item/computer_hardware/hard_drive/small/integrated/install_default_programs() + store_file(new /datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar + store_file(new /datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. + store_file(new /datum/computer_file/program/robotact(src)) + // Syndicate variant - very slight better /obj/item/computer_hardware/hard_drive/small/syndicate diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 04bf494fe4..5a65184a27 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -77,3 +77,23 @@ power_usage = 100 // Better range but higher power usage. icon_state = "net_wired" w_class = WEIGHT_CLASS_NORMAL + +/obj/item/computer_hardware/network_card/integrated //Borg tablet version, only works while the borg has power and is not locked + name = "cyborg data link" + +/obj/item/computer_hardware/network_card/integrated/get_signal(specific_action = 0) + var/obj/item/modular_computer/tablet/integrated/modularInterface = holder + + if(!modularInterface || !istype(modularInterface)) + return FALSE //wrong type of tablet + + if(!modularInterface.borgo) + return FALSE //No borg found + + if(modularInterface.borgo.lockcharge) + return FALSE //lockdown restricts borg networking + + if(!modularInterface.borgo.cell || modularInterface.borgo.cell.charge == 0) + return FALSE //borg cell dying restricts borg networking + + return ..() diff --git a/code/modules/modular_computers/hardware/portable_disk.dm b/code/modules/modular_computers/hardware/portable_disk.dm index 89b0382e86..f1c565188f 100644 --- a/code/modules/modular_computers/hardware/portable_disk.dm +++ b/code/modules/modular_computers/hardware/portable_disk.dm @@ -4,7 +4,7 @@ power_usage = 10 icon_state = "datadisk6" w_class = WEIGHT_CLASS_TINY - critical = 0 + critical = FALSE max_capacity = 16 device_type = MC_SDD diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm index 13ae6c1f39..ecfbf4c6b2 100644 --- a/code/modules/modular_computers/hardware/recharger.dm +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -6,8 +6,8 @@ /obj/item/computer_hardware/recharger/proc/use_power(amount, charging=0) if(charging) - return 1 - return 0 + return TRUE + return FALSE /obj/item/computer_hardware/recharger/process() ..() @@ -23,28 +23,28 @@ holder.give_power(charge_rate * GLOB.CELLRATE) -/obj/item/computer_hardware/recharger/APC +/obj/item/computer_hardware/recharger/apc_recharger name = "area power connector" desc = "A device that wirelessly recharges connected device from nearby APC." icon_state = "charger_APC" w_class = WEIGHT_CLASS_SMALL // Can't be installed into tablets/PDAs -/obj/item/computer_hardware/recharger/APC/use_power(amount, charging=0) +/obj/item/computer_hardware/recharger/apc_recharger/use_power(amount, charging=0) if(ismachinery(holder.physical)) var/obj/machinery/M = holder.physical if(M.powered()) M.use_power(amount) - return 1 + return TRUE else var/area/A = get_area(src) if(!istype(A)) - return 0 + return FALSE if(A.powered(EQUIP)) A.use_power(amount, EQUIP) - return 1 - return 0 + return TRUE + return FALSE /obj/item/computer_hardware/recharger/wired name = "wired power connector" @@ -56,27 +56,34 @@ if(ismachinery(M.physical) && M.physical.anchored) return ..() to_chat(user, "\The [src] is incompatible with portable computers!") - return 0 + return FALSE /obj/item/computer_hardware/recharger/wired/use_power(amount, charging=0) if(ismachinery(holder.physical) && holder.physical.anchored) var/obj/machinery/M = holder.physical var/turf/T = M.loc if(!T || !istype(T)) - return 0 + return FALSE var/obj/structure/cable/C = T.get_cable_node() if(!C || !C.powernet) - return 0 + return FALSE var/power_in_net = C.powernet.avail-C.powernet.load if(power_in_net && power_in_net > amount) C.powernet.load += amount - return 1 + return TRUE + return FALSE - return 0 +/// This recharger exists only in borg built-in tablets. I would have tied it to the borg's cell but +/// the program that displays laws should always be usable, and the exceptions were starting to pile. +/obj/item/computer_hardware/recharger/cyborg + name = "modular interface power harness" + desc = "A standard connection to power a small computer device from a cyborg's chassis." +/obj/item/computer_hardware/recharger/cyborg/use_power(amount, charging=0) + return TRUE // This is not intended to be obtainable in-game. Intended for adminbus and debugging purposes. diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index 83bb057d66..2cc4da6f9d 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -100,7 +100,7 @@ if(dev_apc_recharger) total_price += 399 if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/recharger/APC) + fabricated_laptop.install_component(new /obj/item/computer_hardware/recharger/apc_recharger) if(dev_printer) total_price += 99 if(fabricate) @@ -169,8 +169,9 @@ /obj/machinery/lapvend/ui_act(action, params) - if(..()) - return TRUE + . = ..() + if(.) + return switch(action) if("pick_device") @@ -224,7 +225,7 @@ return FALSE /obj/machinery/lapvend/ui_interact(mob/user, datum/tgui/ui) - if(stat & (BROKEN | NOPOWER | MAINT)) + if(machine_stat & (BROKEN | NOPOWER | MAINT)) if(ui) ui.close() return FALSE diff --git a/tgstation.dme b/tgstation.dme index edfa146354..b8ab2a32fe 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2846,6 +2846,7 @@ #include "code\modules\modular_computers\file_system\programs\arcade.dm" #include "code\modules\modular_computers\file_system\programs\atmosscan.dm" #include "code\modules\modular_computers\file_system\programs\borg_monitor.dm" +#include "code\modules\modular_computers\file_system\programs\budgetordering.dm" #include "code\modules\modular_computers\file_system\programs\card.dm" #include "code\modules\modular_computers\file_system\programs\cargobounty.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" @@ -2858,6 +2859,8 @@ #include "code\modules\modular_computers\file_system\programs\powermonitor.dm" #include "code\modules\modular_computers\file_system\programs\radar.dm" #include "code\modules\modular_computers\file_system\programs\robocontrol.dm" +#include "code\modules\modular_computers\file_system\programs\robotact.dm" +#include "code\modules\modular_computers\file_system\programs\secureye.dm" #include "code\modules\modular_computers\file_system\programs\sm_monitor.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\contract_uplink.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"