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 = {"