diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 78a359e96798..aa21431577c6 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -254,6 +254,12 @@
#define COMSIG_MACHINERY_POWER_LOST "machinery_power_lost" //from base power_change() when power is lost
#define COMSIG_MACHINERY_POWER_RESTORED "machinery_power_restored" //from base power_change() when power is restored
+// /obj/machinery/power/supermatter_crystal signals
+/// from /obj/machinery/power/supermatter_crystal/process_atmos(); when the SM delam reaches the point of sounding alarms
+#define COMSIG_SUPERMATTER_DELAM_START_ALARM "sm_delam_start_alarm"
+/// from /obj/machinery/power/supermatter_crystal/process_atmos(); when the SM sounds an audible alarm
+#define COMSIG_SUPERMATTER_DELAM_ALARM "sm_delam_alarm"
+
// /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index dd9d7b9d9935..40b7a0d550d1 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -213,6 +213,8 @@ GLOBAL_LIST_INIT(heavyfootmob, typecacheof(list(
#define isitem(A) (istype(A, /obj/item))
+#define isidcard(I) (istype(I, /obj/item/card/id))
+
#define isstructure(A) (istype(A, /obj/structure))
#define ismachinery(A) (istype(A, /obj/machinery))
diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm
index 9bc081b282af..89077b48d904 100644
--- a/code/__DEFINES/machines.dm
+++ b/code/__DEFINES/machines.dm
@@ -39,11 +39,13 @@
#define MC_HDD "HDD"
#define MC_SDD "SDD"
#define MC_CARD "CARD"
+#define MC_CARD2 "CARD2"
#define MC_NET "NET"
#define MC_PRINT "PRINT"
#define MC_CELL "CELL"
#define MC_CHARGE "CHARGE"
#define MC_AI "AI"
+#define MC_SENSORS "SENSORS"
//NTNet stuff, for modular computers
// NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc)
@@ -71,6 +73,12 @@
#define PROGRAM_STATE_KILLED 0
#define PROGRAM_STATE_BACKGROUND 1
#define PROGRAM_STATE_ACTIVE 2
+//Program categories
+#define PROGRAM_CATEGORY_CREW "Crew"
+#define PROGRAM_CATEGORY_ENGI "Engineering"
+#define PROGRAM_CATEGORY_ROBO "Robotics"
+#define PROGRAM_CATEGORY_SUPL "Supply"
+#define PROGRAM_CATEGORY_MISC "Other"
#define FIREDOOR_OPEN 1
#define FIREDOOR_CLOSED 2
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 85a29457eb45..ab008783b7f9 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -64,7 +64,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
/obj/machinery/computer/card/attackby(obj/O, mob/user, params)//TODO:SANITY
- if(istype(O, /obj/item/card/id))
+ if(isidcard(O))
var/obj/item/card/id/idcard = O
if(!modify)
if (!user.transferItemToLoc(idcard,src))
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index c7782039530e..c8dab14af0df 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -48,11 +48,6 @@
detail_overlay.color = detail_color
add_overlay(detail_overlay)
-/obj/item/proc/GetCard()
-
-/obj/item/card/data/GetCard()
- return src
-
/obj/item/card/data/full_color
desc = "A plastic magstripe card for simple and speedy data storage and transfer. This one has the entire card colored."
icon_state = "data_2"
@@ -256,6 +251,9 @@
/obj/item/card/id/GetID()
return src
+/obj/item/card/id/RemoveID()
+ return src
+
/*
Usage:
update_label()
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 4068cbc0473e..cba668cf85e4 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -160,6 +160,18 @@ GLOBAL_LIST_EMPTY(PDAs)
/obj/item/pda/GetID()
return id
+/obj/item/pda/RemoveID()
+ return do_remove_id()
+
+/obj/item/pda/InsertID(obj/item/inserting_item)
+ var/obj/item/card/inserting_id = inserting_item.RemoveID()
+ if(!inserting_id)
+ return
+ insert_id(inserting_id)
+ if(id == inserting_id)
+ return TRUE
+ return FALSE
+
/obj/item/pda/update_icon()
cut_overlays()
var/mutable_appearance/overlay = new()
@@ -696,16 +708,27 @@ GLOBAL_LIST_EMPTY(PDAs)
if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
+ do_remove_id(usr)
+
+
+/obj/item/pda/proc/do_remove_id(mob/user)
+ if(!id)
+ return
+ if(user)
+ user.put_in_hands(id)
+ to_chat(user, "You remove the ID from the [name].")
+ else
+ id.forceMove(get_turf(src))
+
+ . = id
+ id = null
+ update_icon()
+
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ if(H.wear_id == src)
+ H.sec_hud_set_ID()
- if (id)
- usr.put_in_hands(id)
- to_chat(usr, span_notice("You remove the ID from the [name]."))
- id = null
- update_icon()
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- if(H.wear_id == src)
- H.sec_hud_set_ID()
/obj/item/pda/proc/msg_input(mob/living/U = usr)
var/t = stripped_input(U, "Please enter message", name)
@@ -923,20 +946,28 @@ GLOBAL_LIST_EMPTY(PDAs)
if(istype(C))
I = C
- if(I && I.registered_name)
+ if(I && I?.registered_name)
if(!user.transferItemToLoc(I, src))
return FALSE
- var/obj/old_id = id
- id = I
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- if(H.wear_id == src)
- H.sec_hud_set_ID()
- if(old_id)
- user.put_in_hands(old_id)
+ insert_id(I, user)
update_icon()
return TRUE
+
+/obj/item/pda/proc/insert_id(obj/item/card/id/inserting_id, mob/user)
+ var/obj/old_id = id
+ id = inserting_id
+ if(ishuman(loc))
+ var/mob/living/carbon/human/human_wearer = loc
+ if(human_wearer.wear_id == src)
+ human_wearer.sec_hud_set_ID()
+ if(old_id)
+ if(user)
+ user.put_in_hands(old_id)
+ else
+ old_id.forceMove(get_turf(src))
+
+
// access to status display signals
/obj/item/pda/attackby(obj/item/C, mob/user, params)
if(istype(C, /obj/item/cartridge) && !cartridge)
diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm
index 2ab66c509a48..9b2a65ec972a 100644
--- a/code/game/objects/items/storage/wallets.dm
+++ b/code/game/objects/items/storage/wallets.dm
@@ -71,6 +71,21 @@
/obj/item/storage/wallet/GetID()
return front_id
+/obj/item/storage/wallet/RemoveID()
+ if(!front_id)
+ return
+ . = front_id
+ front_id.forceMove(get_turf(src))
+
+/obj/item/storage/wallet/InsertID(obj/item/inserting_item)
+ var/obj/item/card/inserting_id = inserting_item.RemoveID()
+ if(!inserting_id)
+ return FALSE
+ attackby(inserting_id)
+ if(inserting_id in contents)
+ return TRUE
+ return FALSE
+
/obj/item/storage/wallet/GetAccess()
if(LAZYLEN(combined_access))
return combined_access
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index b1623cc3d18a..66585f220bfa 100644
--- a/code/modules/jobs/access.dm
+++ b/code/modules/jobs/access.dm
@@ -37,6 +37,12 @@
/obj/item/proc/GetID()
return null
+/obj/item/proc/RemoveID()
+ return null
+
+/obj/item/proc/InsertID()
+ return FALSE
+
/// Convert a text string to a list of accesses
/obj/proc/text2access(access_text)
. = list()
diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm
index 3679f7e36554..e3f1d4f97836 100644
--- a/code/modules/jobs/job_types/atmospheric_technician.dm
+++ b/code/modules/jobs/job_types/atmospheric_technician.dm
@@ -46,7 +46,7 @@
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
box = /obj/item/storage/box/engineer
pda_slot = SLOT_L_STORE
- backpack_contents = list(/obj/item/modular_computer/tablet/phone/preset/advanced=1)
+ backpack_contents = list(/obj/item/modular_computer/tablet/phone/preset/advanced/atmos=1)
/datum/outfit/job/atmos/rig
name = "Atmospheric Technician (Hardsuit)"
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index 33b311885427..014f0e9ff4c6 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -45,7 +45,7 @@
alt_shoes = /obj/item/clothing/shoes/xeno_wraps/command // Provides Command shoes to digitigrade species
head = /obj/item/clothing/head/hardhat/white
gloves = /obj/item/clothing/gloves/color/black/ce
- backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) //yogs - removes eng budget
+ backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/phone/preset/advanced/command=1) //yogs - removes eng budget
glasses = /obj/item/clothing/glasses/meson/sunglasses
backpack = /obj/item/storage/backpack/industrial
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index e1de06758588..b2e8b2f3178a 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -50,7 +50,7 @@
l_hand = /obj/item/storage/firstaid/medical
suit_store = /obj/item/flashlight/pen/paramedic
glasses = /obj/item/clothing/glasses/hud/health/sunglasses
- backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) //yogs - removes med budget
+ backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/phone/preset/advanced/command=1) //yogs - removes med budget
backpack = /obj/item/storage/backpack/medic
satchel = /obj/item/storage/backpack/satchel/med
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 29df1103c986..f86904617a4d 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -55,6 +55,6 @@
shoes = /obj/item/clothing/shoes/sneakers/brown
head = /obj/item/clothing/head/hopcap
backpack_contents = list(/obj/item/storage/box/ids=1,\
- /obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) //yogs - removes serv budget
+ /obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/phone/preset/advanced/command=1) //yogs - removes serv budget
chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/hop)
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index ae1265c89b35..738522905314 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -56,7 +56,7 @@
suit_store = /obj/item/gun/energy/e_gun
r_pocket = /obj/item/assembly/flash/handheld
l_pocket = /obj/item/restraints/handcuffs
- backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/modular_computer/tablet/preset/advanced=1) //yogs - removed departmental budget ID //come here often?
+ backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/modular_computer/tablet/phone/preset/advanced/command=1) //yogs - removed departmental budget ID //come here often?
backpack = /obj/item/storage/backpack/security
satchel = /obj/item/storage/backpack/satchel/sec
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index 7c5401df6192..117437600ce0 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -54,7 +54,7 @@
suit = /obj/item/clothing/suit/toggle/labcoat
l_hand = /obj/item/clipboard
l_pocket = /obj/item/laser_pointer
- backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced=1) //yogs - removes sci budget
+ backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/phone/preset/advanced/command=1) //yogs - removes sci budget
backpack = /obj/item/storage/backpack/science
satchel = /obj/item/storage/backpack/satchel/tox
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 2f899e1e2676..b329ff9fcfcd 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -75,12 +75,8 @@
. = pda.owner
else if(istype(tablet))
var/obj/item/computer_hardware/card_slot/card_slot = tablet.all_components[MC_CARD]
- if(card_slot && (card_slot.stored_card2 || card_slot.stored_card))
- if(card_slot.stored_card2) //The second card is the one used for authorization in the ID changing program, so we prioritize it here for consistency
- . = card_slot.stored_card2.registered_name
- else
- if(card_slot.stored_card)
- . = card_slot.stored_card.registered_name
+ if(card_slot?.stored_card)
+ . = card_slot.stored_card.registered_name
if(!.)
. = if_no_id //to prevent null-names making the mob unclickable
return
@@ -114,6 +110,12 @@
if(id_card)
return id_card
+/mob/living/carbon/human/get_id_in_hand()
+ var/obj/item/held_item = get_active_held_item()
+ if(!held_item)
+ return
+ return held_item.GetID()
+
/mob/living/carbon/human/IsAdvancedToolUser()
if(HAS_TRAIT(src, TRAIT_MONKEYLIKE))
return FALSE
@@ -272,4 +274,4 @@
WRITE_FILE(F["current_scar_index"], sanitize_integer(scar_index))
/mob/living/carbon/human/get_biological_state()
- return dna.species.get_biological_state()
\ No newline at end of file
+ return dna.species.get_biological_state()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d01e789aa4c2..9eac34b701b9 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1174,6 +1174,9 @@
/mob/proc/get_idcard(hand_first)
return
+/mob/proc/get_id_in_hand()
+ return
+
/**
* Get the mob VV dropdown extras
*/
diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm
index 61e214893029..953393439b08 100644
--- a/code/modules/modular_computers/computers/_modular_computer_shared.dm
+++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm
@@ -44,18 +44,20 @@
. += "It has a slot installed for an intelliCard."
var/obj/item/computer_hardware/card_slot/card_slot = get_modular_computer_part(MC_CARD)
+ var/obj/item/computer_hardware/card_slot/card_slot2 = get_modular_computer_part(MC_CARD2)
+ var/multiple_slots = istype(card_slot) && istype(card_slot2)
if(card_slot)
- if(card_slot.stored_card || card_slot.stored_card2)
+ 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_slot.stored_card2
+ 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 two slots for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]."
+ . += "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]"]."
else
- . += "It has two slots for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
+ . += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
. += span_info("Alt-click [src] to eject the identification card[multiple_cards ? "s":""].")
else
- . += "It has two slots installed for identification cards."
+ . += "It has [multiple_slots ? "two slots" : "a slot"] installed for identification cards."
var/obj/item/computer_hardware/printer/printer_slot = get_modular_computer_part(MC_PRINT)
if(printer_slot)
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 443fff0a6bac..be3c25330f87 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -7,6 +7,7 @@
var/enabled = 0 // Whether the computer is turned on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
+ var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0
@@ -33,11 +34,12 @@
max_integrity = 100
armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0)
- // Important hardware (must be installed for computer to work)
-
- // Optional hardware (improves functionality, but is not critical for computer to work)
-
- var/list/all_components = list() // List of "connection ports" in this computer and the components with which they are plugged
+ /// List of "connection ports" in this computer and the components with which they are plugged
+ var/list/all_components = list()
+ /// Lazy List of extra hardware slots that can be used modularly.
+ var/list/expansion_bays
+ /// Number of total expansion bays this computer has available.
+ var/max_bays = 0
var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with.
var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks.
@@ -69,61 +71,22 @@
physical = null
return ..()
+/obj/item/modular_computer/attack(atom/A, mob/living/user, params)
+ if(active_program?.tap(A, user, params))
+ user.do_attack_animation(A) //Emulate this animation since we kill the attack in three lines
+ playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1) //Likewise for the tap sound
+ addtimer(CALLBACK(src, .proc/play_ping), 0.5 SECONDS, TIMER_UNIQUE) //Slightly delayed ping to indicate success
+ return
+ return ..()
-/obj/item/modular_computer/proc/add_verb(var/path)
- switch(path)
- if(MC_CARD)
- verbs += /obj/item/modular_computer/proc/eject_id
- if(MC_SDD)
- verbs += /obj/item/modular_computer/proc/eject_disk
- if(MC_AI)
- verbs += /obj/item/modular_computer/proc/eject_card
-
-/obj/item/modular_computer/proc/remove_verb(path)
- switch(path)
- if(MC_CARD)
- verbs -= /obj/item/modular_computer/proc/eject_id
- if(MC_SDD)
- verbs -= /obj/item/modular_computer/proc/eject_disk
- if(MC_AI)
- verbs -= /obj/item/modular_computer/proc/eject_card
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/item/modular_computer/proc/eject_id()
- set name = "Eject ID"
- set category = "Object"
- set src in view(1)
-
- if(issilicon(usr))
- return
- var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- if(usr.canUseTopic(src, BE_CLOSE))
- card_slot.try_eject(null, usr)
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/item/modular_computer/proc/eject_card()
- set name = "Eject Intellicard"
- set category = "Object"
-
- if(issilicon(usr))
- return
- var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI]
- if(usr.canUseTopic(src, BE_CLOSE))
- ai_slot.try_eject(null, usr,1)
-
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/item/modular_computer/proc/eject_disk()
- set name = "Eject Data Disk"
- set category = "Object"
-
- if(issilicon(usr))
- return
- if(usr.canUseTopic(src, BE_CLOSE))
- var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
- if(uninstall_component(portable_drive, usr))
- portable_drive.verb_pickup()
+/**
+ * Plays a ping sound.
+ *
+ * Timers runtime if you try to make them call playsound. Yep.
+ */
+/obj/item/modular_computer/proc/play_ping()
+ playsound(loc, 'sound/machines/ping.ogg', get_clamped_volume(), FALSE, -1)
/obj/item/modular_computer/AltClick(mob/user)
..()
@@ -131,17 +94,9 @@
return
if(user.canUseTopic(src, BE_CLOSE))
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
- var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI]
- var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
- if(portable_drive)
- if(uninstall_component(portable_drive, user))
- portable_drive.verb_pickup()
- else
- if(card_slot && card_slot.try_eject(null, user))
- return
- if(ai_slot)
- ai_slot.try_eject(null, user)
+ return (card_slot2?.try_eject(user) || card_slot?.try_eject(user)) //Try the secondary one first.
// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs.
@@ -151,12 +106,48 @@
return card_slot.GetAccess()
return ..()
+/obj/item/modular_computer/RemoveID()
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
+ var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
+ return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first.
+
+/obj/item/modular_computer/InsertID(obj/item/inserting_item)
+ var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
+ var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
+ if(!(card_slot || card_slot2))
+ //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()
+ if(!inserting_id)
+ return FALSE
+
+ 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/GetID()
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
if(card_slot)
return card_slot.GetID()
return ..()
+/obj/item/modular_computer/RemoveID()
+ var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
+ if(!card_slot)
+ return
+ return card_slot.RemoveID()
+
+/obj/item/modular_computer/InsertID(obj/item/inserting_item)
+ var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
+ if(!card_slot)
+ return FALSE
+ var/obj/item/card/inserting_id = inserting_item.RemoveID()
+ if(!inserting_id)
+ return FALSE
+ return card_slot.try_insert(inserting_id)
+
/obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location)
var/mob/M = usr
if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src, BE_CLOSE))
@@ -178,13 +169,22 @@
turn_on(user)
/obj/item/modular_computer/emag_act(mob/user)
- if(obj_flags & EMAGGED)
- to_chat(user, span_warning("\The [src] was already emagged."))
- return 0
- else
- obj_flags |= EMAGGED
- to_chat(user, span_notice("You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message."))
- return 1
+ if(!enabled)
+ to_chat(user, "You'd need to turn the [src] on first.")
+ return FALSE
+ obj_flags |= EMAGGED //Mostly for consistancy purposes; the programs will do their own emag handling
+ var/newemag = FALSE
+ var/obj/item/computer_hardware/hard_drive/drive = all_components[MC_HDD]
+ for(var/datum/computer_file/program/app in drive.stored_files)
+ if(!istype(app))
+ continue
+ if(app.run_emag())
+ newemag = TRUE
+ if(newemag)
+ to_chat(user, "You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past.")
+ return TRUE
+ to_chat(user, "You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it.")
+ return FALSE
/obj/item/modular_computer/examine(mob/user)
. = ..()
@@ -282,10 +282,34 @@
handle_power() // 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()
+ data["PC_device_theme"] = device_theme
+
var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL]
var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE]
diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm
index 106ac9d19b58..b328e6701e27 100644
--- a/code/modules/modular_computers/computers/item/computer_components.dm
+++ b/code/modules/modular_computers/computers/item/computer_components.dm
@@ -6,6 +6,14 @@
to_chat(user, span_warning("This component is too large for \the [src]!"))
return FALSE
+ if(H.expansion_hw)
+ if(LAZYLEN(expansion_bays) >= max_bays)
+ to_chat(user, "All of the computer's expansion bays are filled.")
+ return FALSE
+ if(LAZYACCESS(expansion_bays, H.device_type))
+ to_chat(user, "The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\".")
+ return FALSE
+
if(all_components[H.device_type])
to_chat(user, span_warning("This computer's hardware slot is already occupied by \the [all_components[H.device_type]]."))
return FALSE
@@ -20,6 +28,8 @@
if(user && !user.transferItemToLoc(H, src))
return FALSE
+ if(H.expansion_hw)
+ LAZYSET(expansion_bays, H.device_type, H)
all_components[H.device_type] = H
to_chat(user, span_notice("You install \the [H] into \the [src]."))
@@ -32,7 +42,9 @@
/obj/item/modular_computer/proc/uninstall_component(obj/item/computer_hardware/H, mob/living/user = null)
if(H.holder != src) // Not our component at all.
return FALSE
+ if(H.expansion_hw)
+ LAZYREMOVE(expansion_bays, H.device_type)
all_components.Remove(H.device_type)
to_chat(user, span_notice("You remove \the [H] from \the [src]."))
@@ -43,6 +55,7 @@
if(enabled && !use_power())
shutdown_computer()
update_icon()
+ return TRUE
// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index e8bcde73ca6a..3b75662ee05a 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -6,17 +6,17 @@
if(!enabled)
if(ui)
ui.close()
- return 0
+ return FALSE
if(!use_power())
if(ui)
ui.close()
- return 0
+ return FALSE
// 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 1
+ return FALSE
+ return TRUE
// Operates TGUI
/obj/item/modular_computer/ui_interact(mob/user, datum/tgui/ui)
@@ -38,22 +38,54 @@
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
- ui = new(user, src, "NtosMain")
- ui.set_autoupdate(TRUE)
- ui.open()
- ui.send_asset(get_asset_datum(/datum/asset/simple/headers))
+ var/headername
+ switch(device_theme)
+ if("ntos")
+ headername = "NtOS Main Menu"
+ if("syndicate")
+ headername = "Syndix Main Menu"
+ ui = new(user, src, "NtosMain", headername, 400, 500)
+ if(ui.open())
+ ui.send_asset(get_asset_datum(/datum/asset/simple/headers))
/obj/item/modular_computer/ui_data(mob/user)
var/list/data = get_header_data()
+ data["device_theme"] = device_theme
+ data["login"] = list()
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD]
+ if(cardholder)
+ var/obj/item/card/id/stored_card = cardholder.GetID()
+ if(stored_card)
+ var/stored_name = stored_card.registered_name
+ var/stored_title = stored_card.assignment
+ if(!stored_name)
+ stored_name = "Unknown"
+ if(!stored_title)
+ stored_title = "Unknown"
+ data["login"] = list(
+ IDName = stored_name,
+ IDJob = stored_title,
+ )
+
+ data["removable_media"] = list()
+ if(all_components[MC_SDD])
+ data["removable_media"] += "removable storage disk"
+ var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI]
+ if(intelliholder?.stored_card)
+ data["removable_media"] += "intelliCard"
+ var/obj/item/computer_hardware/card_slot/secondarycardholder = all_components[MC_CARD2]
+ if(secondarycardholder?.stored_card)
+ data["removable_media"] += "secondary RFID card"
+
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
@@ -69,10 +101,10 @@
switch(action)
if("PC_exit")
kill_program()
- return 1
+ return TRUE
if("PC_shutdown")
shutdown_computer()
- return 1
+ return TRUE
if("PC_minimize")
var/mob/user = usr
if(!active_program || !all_components[MC_CPU])
@@ -119,6 +151,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
@@ -134,8 +167,9 @@
return
if(P.run_program(user))
active_program = P
+ P.alert_pending = FALSE
update_icon()
- return 1
+ return TRUE
if("PC_toggle_light")
light_on = !light_on
@@ -159,6 +193,36 @@
light_color = new_color
update_light()
return TRUE
+
+ if("PC_Eject_Disk")
+ var/param = params["name"]
+ var/mob/user = usr
+ switch(param)
+ if("removable storage disk")
+ var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD]
+ if(!portable_drive)
+ return
+ if(uninstall_component(portable_drive, usr))
+ user.put_in_hands(portable_drive)
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50)
+ if("intelliCard")
+ var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI]
+ if(!intelliholder)
+ return
+ if(intelliholder.try_eject(user))
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50)
+ if("ID")
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD]
+ if(!cardholder)
+ return
+ cardholder.try_eject(user)
+ if("secondary RFID card")
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD2]
+ if(!cardholder)
+ return
+ cardholder.try_eject(user)
+
+
else
return
diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm
index cd40143ac2b0..82ede8ccad77 100644
--- a/code/modules/modular_computers/computers/item/laptop.dm
+++ b/code/modules/modular_computers/computers/item/laptop.dm
@@ -11,6 +11,7 @@
hardware_flag = PROGRAM_LAPTOP
max_hardware_size = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_NORMAL
+ max_bays = 4
// No running around with open laptops in hands.
item_flags = SLOWS_WHILE_IN_HAND
diff --git a/code/modules/modular_computers/computers/item/phone.dm b/code/modules/modular_computers/computers/item/phone.dm
index 3906bb8ff16b..b0374a406d73 100644
--- a/code/modules/modular_computers/computers/item/phone.dm
+++ b/code/modules/modular_computers/computers/item/phone.dm
@@ -8,5 +8,6 @@
hardware_flag = PROGRAM_PHONE
max_hardware_size = WEIGHT_CLASS_TINY
w_class = WEIGHT_CLASS_SMALL
+ max_bays = 2
steel_sheet_cost = 1
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm
index 5a93c656f593..4ab6264a6ee0 100644
--- a/code/modules/modular_computers/computers/item/processor.dm
+++ b/code/modules/modular_computers/computers/item/processor.dm
@@ -58,23 +58,11 @@
machinery_computer.update_icon()
return
-/obj/item/modular_computer/processor/add_verb(path)
- switch(path)
- if(MC_CARD)
- machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_id
- if(MC_SDD)
- machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_disk
- if(MC_AI)
- machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_card
-
-/obj/item/modular_computer/processor/remove_verb(path)
- switch(path)
- if(MC_CARD)
- machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_id
- if(MC_SDD)
- machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk
- if(MC_AI)
- machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card
-
/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 687b942462dd..3c6216a51975 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -9,6 +9,7 @@
hardware_flag = PROGRAM_TABLET
max_hardware_size = WEIGHT_CLASS_SMALL
w_class = WEIGHT_CLASS_NORMAL
+ max_bays = 3
steel_sheet_cost = 1
slot_flags = ITEM_SLOT_BELT
has_light = TRUE //LED flashlight!
@@ -25,6 +26,7 @@
icon_state_unpowered = "[icon_state_base]-[finish_color]"
icon_state_powered = "[icon_state_base]-[finish_color]"
+
/obj/item/modular_computer/tablet/syndicate_contract_uplink
name = "contractor tablet"
icon = 'icons/obj/contractor_tablet.dmi'
@@ -36,3 +38,16 @@
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
comp_light_luminosity = 6.3
has_variants = FALSE
+
+/// Given to Nuke Ops members.
+/obj/item/modular_computer/tablet/nukeops
+ comp_light_luminosity = 6.3
+ finish_color = "red"
+ device_theme = "syndicate"
+
+/obj/item/modular_computer/tablet/nukeops/emag_act(mob/user)
+ if(!enabled)
+ to_chat(user, "You'd need to turn the [src] on first.")
+ 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
diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm
index d818377226b4..2697a7ab4f8c 100644
--- a/code/modules/modular_computers/computers/item/tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/tablet_presets.dm
@@ -25,10 +25,20 @@
install_component(new /obj/item/computer_hardware/processor_unit/small)
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)
+ 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)
-// A tablet made by the Syndacate for use by their comms agents, has a Syndicate hard drive, is emagged, and is red because Syndicate
+/obj/item/modular_computer/tablet/preset/advanced/atmos/Initialize() //This will be defunct and will be replaced when NtOS PDAs are done
+ . = ..()
+ install_component(new /obj/item/computer_hardware/sensorpackage)
+
+/obj/item/modular_computer/tablet/preset/advanced/command/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/sensorpackage)
+ install_component(new /obj/item/computer_hardware/card_slot/secondary)
+
+/// A simple syndicate tablet, used for comms agents
/obj/item/modular_computer/tablet/preset/syndicate/Initialize()
. = ..()
finish_color = "red"
@@ -36,8 +46,8 @@
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/syndicate)
install_component(new /obj/item/computer_hardware/network_card/advanced)
+ install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/printer/mini)
- obj_flags |= EMAGGED
update_icon()
/// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink.
@@ -59,6 +69,14 @@
install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/printer/mini)
+/// Given to Nuke Ops members.
+/obj/item/modular_computer/tablet/nukeops/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/processor_unit/small)
+ 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)
+
//Phone Presets//
// This is literally the worst possible cheap phone
@@ -87,5 +105,14 @@
install_component(new /obj/item/computer_hardware/processor_unit/small)
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)
+ 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)
+
+/obj/item/modular_computer/tablet/phone/preset/advanced/atmos/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/sensorpackage)
+
+/obj/item/modular_computer/tablet/phone/preset/advanced/command/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/card_slot/secondary)
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 8b4fcc27ed1f..1a2d724e2c8f 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -1,6 +1,6 @@
/obj/machinery/modular_computer/console/preset
// Can be changed to give devices specific hardware
- var/_has_id_slot = FALSE
+ var/_has_second_id_slot = FALSE
var/_has_printer = FALSE
var/_has_battery = FALSE
var/_has_ai = FALSE
@@ -11,8 +11,9 @@
return
cpu.install_component(new /obj/item/computer_hardware/processor_unit)
- if(_has_id_slot)
- cpu.install_component(new /obj/item/computer_hardware/card_slot)
+ cpu.install_component(new /obj/item/computer_hardware/card_slot)
+ if(_has_second_id_slot)
+ cpu.install_component(new /obj/item/computer_hardware/card_slot/secondary)
if(_has_printer)
cpu.install_component(new /obj/item/computer_hardware/printer)
if(_has_battery)
@@ -59,7 +60,7 @@
console_department = "Command"
name = "command console"
desc = "A stationary computer. This one comes preloaded with command programs."
- _has_id_slot = TRUE
+ _has_second_id_slot = TRUE
_has_printer = TRUE
/obj/machinery/modular_computer/console/preset/command/install_programs()
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index 0a1a8e11fd3c..0af703169ba0 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -48,7 +48,10 @@
cpu.attack_ghost(user)
/obj/machinery/modular_computer/emag_act(mob/user)
- return cpu ? cpu.emag_act(user) : 1
+ if(!cpu)
+ to_chat(user, "You'd need to turn the [src] on first.")
+ return FALSE
+ return (cpu.emag_act(user))
/obj/machinery/modular_computer/update_icon()
cut_overlays()
@@ -71,29 +74,6 @@
add_overlay("bsod")
add_overlay("broken")
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/machinery/modular_computer/proc/eject_id()
- set name = "Eject ID"
- set category = "Object"
-
- if(cpu)
- cpu.eject_id()
-
-// Eject ID card from computer, if it has ID slot with card inside.
-/obj/machinery/modular_computer/proc/eject_disk()
- set name = "Eject Data Disk"
- set category = "Object"
-
- if(cpu)
- cpu.eject_disk()
-
-/obj/machinery/modular_computer/proc/eject_card()
- set name = "Eject Intellicard"
- set category = "Object"
- set src in view(1)
-
- if(cpu)
- cpu.eject_card()
/obj/machinery/modular_computer/AltClick(mob/user)
if(cpu)
@@ -133,7 +113,7 @@
. = ..()
/obj/machinery/modular_computer/attackby(var/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 ..()
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index f5d63600b494..15e7f519ca2f 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -15,6 +15,8 @@
var/filedesc = "Unknown Program"
/// Short description of this program's function.
var/extended_desc = "N/A"
+ /// Category in the NTDownloader.
+ var/category = PROGRAM_CATEGORY_MISC
/// Program-specific screen icon state
var/program_icon_state = null
/// Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes.
@@ -35,6 +37,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)
..()
@@ -64,28 +74,51 @@
/datum/computer_file/program/proc/generate_network_log(text)
if(computer)
return computer.add_log(text)
- return 0
+ return FALSE
+
+/**
+ *Runs when the device is used to attack an atom in non-combat mode.
+ *
+ *Simulates using the device to read or scan something. Tap is called by the computer during pre_attack
+ *and sends us all of the related info. If we return TRUE, the computer will stop the attack process
+ *there. What we do with the info is up to us, but we should only return TRUE if we actually perform
+ *an action of some sort.
+ *Arguments:
+ *A is the atom being tapped
+ *user is the person making the attack action
+ *params is anything the pre_attack() proc had in the same-named variable.
+*/
+/datum/computer_file/program/proc/tap(atom/A, mob/living/user, params)
+ return FALSE
/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null)
if(!(hardware_flag & usage_flags))
if(loud && computer && user)
to_chat(user, span_danger("\The [computer] flashes an \"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)
return computer.get_ntnet_status(specific_action)
- return 0
+ return FALSE
// Called by Process() on device that runs us, once every tick.
/datum/computer_file/program/proc/process_tick()
- return 1
-
-// Check if the user can run program. Only humans can operate computer. Automatically called in run_program()
-// User has to wear their ID for ID Scan to work.
-// Can also be called manually, with optional parameter being access_to_check to scan the user's ID
-/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE)
+ 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.
+*/
+/datum/computer_file/program/proc/can_run(mob/user, loud = FALSE, access_to_check, transfer = FALSE, var/list/access)
// Defaults to required_access
if(!access_to_check)
if(transfer && transfer_access)
@@ -104,29 +137,24 @@
if(issilicon(user))
return TRUE
- if(ishuman(user))
+ if(!length(access))
var/obj/item/card/id/D
var/obj/item/computer_hardware/card_slot/card_slot
- if(computer && card_slot)
+ if(computer)
card_slot = computer.all_components[MC_CARD]
- D = card_slot.GetID()
- var/mob/living/carbon/human/h = user
- var/obj/item/card/id/I = h.get_idcard(TRUE)
+ D = card_slot?.GetID()
- if(!I && !D)
+ if(!D)
if(loud)
to_chat(user, span_danger("\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning."))
return FALSE
+ access = D.GetAccess()
- if(I)
- if(access_to_check in I.GetAccess())
- return TRUE
- else if(D)
- if(access_to_check in D.GetAccess())
- return TRUE
- if(loud)
- to_chat(user, span_danger("\The [computer] flashes an \"Access Denied\" warning."))
- return 0
+ if(access_to_check in access)
+ return TRUE
+ if(loud)
+ to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.")
+ return FALSE
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
// always include the device here in this proc. This proc basically relays the request to whatever is running the program.
@@ -142,15 +170,29 @@
if(requires_ntnet && network_destination)
generate_network_log("Connection opened to [network_destination].")
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).
+**/
+/datum/computer_file/program/proc/run_emag()
+ return FALSE
// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client.
/datum/computer_file/program/proc/kill_program(forced = FALSE)
program_state = PROGRAM_STATE_KILLED
if(network_destination)
generate_network_log("Connection to [network_destination] closed.")
- return 1
+ return TRUE
/datum/computer_file/program/ui_interact(mob/user, datum/tgui/ui)
@@ -159,8 +201,8 @@
return
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.
// Calls beginning with "PRG_" are reserved for programs handling.
@@ -168,17 +210,17 @@
// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE.
/datum/computer_file/program/ui_act(action,list/params,datum/tgui/ui)
if(..())
- return 1
+ return TRUE
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/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm
index f1d1ad0bd9a5..25e5fa1cccf1 100644
--- a/code/modules/modular_computers/file_system/program_events.dm
+++ b/code/modules/modular_computers/file_system/program_events.dm
@@ -2,7 +2,7 @@
// Always include a parent call when overriding an event.
// Called when the ID card is removed from computer. ID is removed AFTER this proc.
-/datum/computer_file/program/proc/event_idremoved(background, slot)
+/datum/computer_file/program/proc/event_idremoved(background)
return
// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss.
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
index 217d50acf917..129229c53501 100644
--- a/code/modules/modular_computers/file_system/programs/airestorer.dm
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/aidiag
filename = "aidiag"
filedesc = "AI Integrity Restorer"
+ category = PROGRAM_CATEGORY_ROBO
program_icon_state = "generic"
extended_desc = "This program is capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
size = 12
@@ -9,6 +10,8 @@
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
@@ -48,7 +51,7 @@
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)
- ai_slot.try_eject(0,usr)
+ ai_slot.try_eject(usr)
return TRUE
/datum/computer_file/program/aidiag/process_tick()
diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm
index d5a3ffd4a886..9761f3ccc733 100644
--- a/code/modules/modular_computers/file_system/programs/alarm.dm
+++ b/code/modules/modular_computers/file_system/programs/alarm.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/alarm_monitor
filename = "alarmmonitor"
filedesc = "Alarm Monitor"
+ category = PROGRAM_CATEGORY_ENGI
ui_header = "alarm_green.gif"
program_icon_state = "alert-green"
extended_desc = "This program provides visual interface for station's alarm system."
@@ -8,6 +9,7 @@
network_destination = "alarm monitoring network"
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 935c79b195e3..a98ae04d5036 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
@@ -1,6 +1,7 @@
/datum/computer_file/program/contract_uplink
filename = "contractor uplink"
filedesc = "Syndicate Contractor Uplink"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "assign"
extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
size = 10
@@ -9,6 +10,8 @@
unsendable = 1
undeletable = 1
tgui_id = "SyndContractor"
+ program_icon = "tasks"
+
var/error = ""
var/info_screen = TRUE
var/assigned = FALSE
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 9ad6926f2c77..aa77ed2c50b2 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/ntnet_dos
filename = "ntn_dos"
filedesc = "DoS Traffic Generator"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "hostile"
extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect"
size = 20
@@ -8,6 +9,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
@@ -85,4 +87,4 @@
data["relays"] += list(list("id" = R.uid))
data["focus"] = target ? target.uid : null
- return data
\ No newline at end of file
+ return data
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 b9af7c594a87..d53b6b27f158 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/revelation
filename = "revelation"
filedesc = "Revelation"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "hostile"
extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution."
size = 13
@@ -8,6 +9,7 @@
available_on_ntnet = FALSE
available_on_syndinet = TRUE
tgui_id = "NtosRevelation"
+ program_icon = "magnet"
var/armed = 0
@@ -67,4 +69,4 @@
data["armed"] = armed
- return data
\ No newline at end of file
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm
index b72ce2f99b9e..6c68d629fb8a 100644
--- a/code/modules/modular_computers/file_system/programs/arcade.dm
+++ b/code/modules/modular_computers/file_system/programs/arcade.dm
@@ -7,6 +7,7 @@
network_destination = "arcade network"
size = 6
tgui_id = "NtosArcade"
+ program_icon = "gamepad"
var/game_active = TRUE //Checks to see if a game is in progress.
var/pause_state = FALSE //This disables buttons in order to prevent multiple actions before the opponent's actions.
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index f8c9424f6586..d3a1e5eb04aa 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -1,17 +1,28 @@
/datum/computer_file/program/atmosscan
filename = "atmosscan"
filedesc = "Atmospheric Scanner"
+ category = PROGRAM_CATEGORY_ENGI
program_icon_state = "air"
extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device."
network_destination = "atmos scan"
size = 4
tgui_id = "NtosAtmos"
+ program_icon = "thermometer-half"
+
+/datum/computer_file/program/atmosscan/run_program(mob/living/user)
+ . = ..()
+ if (!.)
+ return
+ if(!computer?.get_modular_computer_part(MC_SENSORS)) //Giving a clue to users why the program is spitting out zeros.
+ to_chat(user, "\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\".")
+
/datum/computer_file/program/atmosscan/ui_data(mob/user)
var/list/data = get_header_data()
var/list/airlist = list()
var/turf/T = get_turf(ui_host())
- if(T)
+ var/obj/item/computer_hardware/sensorpackage/sensors = computer?.get_modular_computer_part(MC_SENSORS)
+ if(T && sensors?.check_functionality())
var/datum/gas_mixture/environment = T.return_air()
var/list/env_gases = environment.get_gases()
var/pressure = environment.return_pressure()
@@ -24,6 +35,10 @@
if(gas_level > 0)
airlist += list(list("name" = "[env_gases[id][GAS_META][META_GAS_NAME]]", "percentage" = round(gas_level*100, 0.01)))
data["AirData"] = airlist
+ else
+ data["AirPressure"] = 0
+ data["AirTemp"] = 0
+ data["AirData"] = list(list())
return data
/datum/computer_file/program/atmosscan/ui_act(action, list/params)
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 790dd2f5b528..34ff0b8f1bd7 100644
--- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/borg_monitor
filename = "cyborgmonitor"
filedesc = "Cyborg Remote Monitoring"
+ category = PROGRAM_CATEGORY_ROBO
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of station cyborgs."
@@ -9,6 +10,7 @@
network_destination = "cyborg remote monitoring"
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()
@@ -83,6 +85,7 @@
/datum/computer_file/program/borg_monitor/syndicate
filename = "scyborgmonitor"
filedesc = "Mission-Specific Cyborg Remote Monitoring"
+ category = PROGRAM_CATEGORY_ROBO
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
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 8ff073a44f2d..2cdc79174bdb 100644
--- a/code/modules/modular_computers/file_system/programs/bounty_board.dm
+++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/bounty_board
filename = "bountyboard"
filedesc = "Bounty Board Request Network"
+ category = PROGRAM_CATEGORY_SUPL
program_icon_state = "bountyboard"
extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.."
requires_ntnet = TRUE
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index f45d0879bfcc..5449551a5857 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -9,13 +9,14 @@
/datum/computer_file/program/card_mod
filename = "cardmod"
filedesc = "ID Card Modification"
+ category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for programming employee ID cards to access parts of the station."
transfer_access = ACCESS_HEADS
requires_ntnet = 0
size = 8
- usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET
tgui_id = "NtosCard"
+ program_icon = "id-card"
var/is_centcom = FALSE
var/minor = FALSE
@@ -99,17 +100,19 @@
return TRUE
var/obj/item/computer_hardware/card_slot/card_slot
+ var/obj/item/computer_hardware/card_slot/card_slot2
var/obj/item/computer_hardware/printer/printer
if(computer)
card_slot = computer.all_components[MC_CARD]
+ card_slot2 = computer.all_components[MC_CARD2]
printer = computer.all_components[MC_PRINT]
- if(!card_slot)
+ if(!card_slot || !card_slot2)
return
var/mob/user = usr
- var/obj/item/card/id/user_id_card = user.get_idcard(FALSE)
+ var/obj/item/card/id/user_id_card = card_slot.stored_card
- var/obj/item/card/id/id_card = card_slot.stored_card
+ var/obj/item/card/id/target_id_card = card_slot2.stored_card
switch(action)
if("PRG_authenticate")
@@ -130,14 +133,14 @@
return
var/contents = {"
Access Report
Prepared By: [user_id_card && user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
- For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
+ For: [target_id_card.registered_name ? target_id_card.registered_name : "Unregistered"]
- Assignment: [id_card.assignment]
+ Assignment: [target_id_card.assignment]
Access:
"}
var/known_access_rights = get_all_accesses()
- for(var/A in id_card.access)
+ for(var/A in target_id_card.access)
if(A in known_access_rights)
contents += " [get_access_desc(A)]"
@@ -149,43 +152,40 @@
computer.visible_message(span_notice("\The [computer] prints out a paper."))
return TRUE
if("PRG_eject")
- if(!computer || !card_slot)
+ if(!computer || !card_slot2)
return
- if(id_card)
- GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment)
- card_slot.try_eject(TRUE, user)
+ if(target_id_card)
+ GLOB.data_core.manifest_modify(target_id_card.registered_name, target_id_card.assignment)
+ return card_slot2.try_eject(user)
else
var/obj/item/I = user.get_active_held_item()
if(istype(I, /obj/item/card/id))
- if(!user.transferItemToLoc(I, computer))
- return
- card_slot.stored_card = I
- playsound(computer, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
- return TRUE
+ return card_slot2.try_insert(I)
+ return FALSE
if("PRG_terminate")
if(!computer || !authenticated)
return
if(minor)
- if(!(id_card.assignment in head_subordinates) && id_card.assignment != "Assistant")
+ if(!(target_id_card.assignment in head_subordinates) && target_id_card.assignment != "Assistant")
return
- id_card.access -= get_all_centcom_access() + get_all_accesses()
- id_card.assignment = "Unassigned"
- id_card.update_label()
+ target_id_card.access -= get_all_centcom_access() + get_all_accesses()
+ target_id_card.assignment = "Unassigned"
+ target_id_card.update_label()
playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
if("PRG_edit")
- if(!computer || !authenticated || !id_card)
+ if(!computer || !authenticated || !target_id_card)
return
var/new_name = params["name"]
if(!new_name)
return
- id_card.registered_name = new_name
- id_card.update_label()
+ target_id_card.registered_name = new_name
+ target_id_card.update_label()
playsound(computer, "terminal_type", 50, FALSE)
return TRUE
if("PRG_assign")
- if(!computer || !authenticated || !id_card)
+ if(!computer || !authenticated || !target_id_card)
return
var/target = params["assign_target"]
if(!target)
@@ -194,8 +194,8 @@
if(target == "Custom")
var/custom_name = params["custom_name"]
if(custom_name)
- id_card.assignment = custom_name
- id_card.update_label()
+ target_id_card.assignment = custom_name
+ target_id_card.update_label()
else
if(minor && !(target in head_subordinates))
return
@@ -213,11 +213,12 @@
to_chat(user, span_warning("No class exists for this job: [target]"))
return
new_access = job.get_access()
- id_card.access -= get_all_centcom_access() + get_all_accesses()
- id_card.access |= new_access
- id_card.originalassignment = target
- id_card.assignment = target
- id_card.update_label()
+ target_id_card.access -= get_all_centcom_access() + get_all_accesses()
+ target_id_card.access |= new_access
+ target_id_card.originalassignment = target
+ target_id_card.assignment = target
+ target_id_card.update_label()
+
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_access")
@@ -225,22 +226,22 @@
return
var/access_type = text2num(params["access_target"])
if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses()))
- if(access_type in id_card.access)
- id_card.access -= access_type
+ if(access_type in target_id_card.access)
+ target_id_card.access -= access_type
else
- id_card.access |= access_type
+ target_id_card.access |= access_type
playsound(computer, "terminal_type", 50, FALSE)
return TRUE
if("PRG_grantall")
if(!computer || !authenticated || minor)
return
- id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses())
+ target_id_card.access |= (is_centcom ? get_all_centcom_access() : get_all_accesses())
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_denyall")
if(!computer || !authenticated || minor)
return
- id_card.access.Cut()
+ target_id_card.access.Cut()
playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
if("PRG_grantregion")
@@ -249,7 +250,7 @@
var/region = text2num(params["region"])
if(isnull(region) || !(region in region_access))
return
- id_card.access |= get_region_accesses(region)
+ target_id_card.access |= get_region_accesses(region)
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_denyregion")
@@ -258,7 +259,7 @@
var/region = text2num(params["region"])
if(isnull(region) || !(region in region_access))
return
- id_card.access -= get_region_accesses(region)
+ target_id_card.access -= get_region_accesses(region)
playsound(computer, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
@@ -323,17 +324,17 @@
/datum/computer_file/program/card_mod/ui_data(mob/user)
var/list/data = get_header_data()
- var/obj/item/computer_hardware/card_slot/card_slot
+ var/obj/item/computer_hardware/card_slot/card_slot2
var/obj/item/computer_hardware/printer/printer
if(computer)
- card_slot = computer.all_components[MC_CARD]
+ 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_slot
+ data["have_id_slot"] = !!(card_slot2)
data["have_printer"] = !!printer
else
data["have_id_slot"] = FALSE
@@ -342,7 +343,7 @@
data["authenticated"] = authenticated
if(computer)
- var/obj/item/card/id/id_card = card_slot.stored_card
+ 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)
diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm
index fae06544d5f9..f4a264797193 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
diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
index 0e9762e69ed2..026bca5d8466 100644
--- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm
+++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm
@@ -1,12 +1,14 @@
/datum/computer_file/program/crew_manifest
filename = "crewmani"
filedesc = "Crew Manifest"
+ category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for viewing and printing the current crew manifest"
transfer_access = ACCESS_HEADS
requires_ntnet = FALSE
size = 4
tgui_id = "NtosCrewManifest"
+ program_icon = "clipboard-list"
/datum/computer_file/program/crew_manifest/ui_static_data(mob/user)
var/list/data = list()
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 aba826fce89d..673f18aa33e2 100644
--- a/code/modules/modular_computers/file_system/programs/file_browser.dm
+++ b/code/modules/modular_computers/file_system/programs/file_browser.dm
@@ -8,6 +8,7 @@
available_on_ntnet = FALSE
undeletable = TRUE
tgui_id = "NtosFileManager"
+ program_icon = "folder"
var/open_file
var/error
@@ -65,6 +66,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 +86,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 bccc6e4dbe21..d9e3e0667581 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -1,12 +1,14 @@
/datum/computer_file/program/job_management
filename = "job_manage"
filedesc = "Job Manager"
+ category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for viewing and changing job slot avalibility."
transfer_access = ACCESS_HEADS
requires_ntnet = 0
size = 4
tgui_id = "NtosJobManager"
+ program_icon = "address-book"
var/change_position_cooldown = 30
//Jobs you cannot open new positions for
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index b743714ca179..f6cd3e1b63b5 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -11,43 +11,66 @@
available_on_ntnet = 0
ui_header = "downloader_finished.gif"
tgui_id = "NtosNetDownloader"
+ program_icon = "download"
var/datum/computer_file/program/downloaded_file = null
- var/hacked_download = 0
+ var/hacked_download = FALSE
var/download_completion = 0 //GQ of downloaded data.
var/download_netspeed = 0
var/downloaderror = ""
var/obj/item/modular_computer/my_computer = null
-
+ var/emagged = FALSE
+ var/list/main_repo
+ var/list/antag_repo
+
+ var/list/show_categories = list(
+ PROGRAM_CATEGORY_CREW,
+ PROGRAM_CATEGORY_ENGI,
+ PROGRAM_CATEGORY_ROBO,
+ PROGRAM_CATEGORY_SUPL,
+ PROGRAM_CATEGORY_MISC,
+ )
+
+
+/datum/computer_file/program/ntnetdownload/run_program()
+ . = ..()
+ main_repo = SSnetworks.station_network.available_station_software
+ antag_repo = SSnetworks.station_network.available_antag_software
+
+/datum/computer_file/program/ntnetdownload/run_emag()
+ if(emagged)
+ return FALSE
+ emagged = TRUE
+ return TRUE
/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 computer. No.
- if(PRG.available_on_syndinet && !(computer.obj_flags & EMAGGED))
- return 0
+ // Attempting to download antag only program, but without having emagged/syndicate computer. No.
+ if(PRG.available_on_syndinet && !emagged)
+ 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 SSnetworks.station_network.available_station_software)
+ if(PRG in main_repo)
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.")
- hacked_download = 0
- else if(PRG in SSnetworks.station_network.available_antag_software)
+ 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()
@@ -90,26 +113,28 @@
/datum/computer_file/program/ntnetdownload/ui_act(action, params)
if(..())
- return 1
+ return TRUE
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
downloaded_file = null
downloaderror = ""
- return 1
- return 0
+ return TRUE
+ return FALSE
/datum/computer_file/program/ntnetdownload/ui_data(mob/user)
my_computer = computer
if(!istype(my_computer))
return
+ var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
+ var/list/access = card_slot?.GetAccess()
var/list/data = get_header_data()
@@ -127,36 +152,29 @@
var/obj/item/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD]
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
- var/list/all_entries[0]
- for(var/A in SSnetworks.station_network.available_station_software)
- 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) || hard_drive.find_file_by_name(P.filename))
- continue
- all_entries.Add(list(list(
+ data["emagged"] = emagged
+
+ var/list/repo = antag_repo | main_repo
+ var/list/program_categories = list()
+
+ for(var/I in repo)
+ var/datum/computer_file/program/P = I
+ if(!(P.category in program_categories))
+ program_categories.Add(P.category)
+ data["programs"] += list(list(
+ "icon" = P.program_icon,
"filename" = P.filename,
"filedesc" = P.filedesc,
"fileinfo" = P.extended_desc,
- "compatibility" = check_compatibility(P),
+ "category" = P.category,
+ "installed" = !!hard_drive.find_file_by_name(P.filename),
+ "compatible" = check_compatibility(P),
"size" = P.size,
- )))
- data["hackedavailable"] = 0
- if(computer.obj_flags & EMAGGED) // If we are running on emagged computer we have access to some "bonus" software
- var/list/hacked_programs[0]
- for(var/S in SSnetworks.station_network.available_antag_software)
- var/datum/computer_file/program/P = S
- if(hard_drive.find_file_by_name(P.filename))
- continue
- data["hackedavailable"] = 1
- hacked_programs.Add(list(list(
- "filename" = P.filename,
- "filedesc" = P.filedesc,
- "fileinfo" = P.extended_desc,
- "size" = P.size,
- )))
- data["hacked_programs"] = hacked_programs
-
- data["downloadable_programs"] = all_entries
+ "access" = emagged && P.available_on_syndinet ? TRUE : P.can_run(user,transfer = 1, access = access),
+ "verifiedsource" = P.available_on_ntnet,
+ ))
+
+ data["categories"] = show_categories & program_categories
return data
@@ -164,9 +182,34 @@
var/hardflag = computer.hardware_flag
if(P && P.is_supported_by_hardware(hardflag,0))
- return "Compatible"
- return "Incompatible!"
+ return TRUE
+ return FALSE
/datum/computer_file/program/ntnetdownload/kill_program(forced)
abort_file_download()
return ..(forced)
+
+/// A simple pre-emagged version
+/datum/computer_file/program/ntnetdownload/emagged
+ emagged = TRUE
+
+////////////////////////
+//Syndicate Downloader//
+////////////////////////
+
+/// This app only lists programs normally found in the emagged section of the normal downloader app
+
+/datum/computer_file/program/ntnetdownload/syndicate
+ filename = "syndownloader"
+ filedesc = "Software Download Tool"
+ program_icon_state = "generic"
+ extended_desc = "This program allows downloads of software from shared Syndicate repositories"
+ requires_ntnet = 0
+ ui_header = "downloader_finished.gif"
+ tgui_id = "NtosNetDownloader"
+ emagged = TRUE
+
+/datum/computer_file/program/ntnetdownload/syndicate/run_program()
+ . = ..()
+ main_repo = SSnetworks.station_network.available_antag_software
+ antag_repo = null
diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
index 9b5cf2e6c451..c0b2f5a46039 100644
--- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/ntnetmonitor
filename = "ntmonitor"
filedesc = "NTNet Diagnostics and Monitoring"
+ category = PROGRAM_CATEGORY_MISC
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,6 +9,7 @@
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(..())
@@ -70,4 +72,4 @@
data["ntnetlogs"] += list(list("entry" = i))
data["ntnetmaxlogs"] = SSnetworks.station_network.setting_maxlogcount
- return data
\ No newline at end of file
+ return data
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 0628942c986d..48d52047b889 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/chatclient
filename = "ntnrc_client"
filedesc = "Chat Client"
+ category = PROGRAM_CATEGORY_MISC
program_icon_state = "command"
extended_desc = "This program allows communication over NTNRC network"
size = 8
@@ -10,6 +11,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
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index bd11474858b6..361d0f724d7f 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -3,6 +3,7 @@
/datum/computer_file/program/power_monitor
filename = "powermonitor"
filedesc = "Power Monitor"
+ category = PROGRAM_CATEGORY_ENGI
program_icon_state = "power_monitor"
extended_desc = "This program connects to sensors around the station to provide information about electrical systems"
ui_header = "power_norm.gif"
@@ -12,6 +13,7 @@
network_destination = "power monitoring system"
size = 9
tgui_id = "NtosPowerMonitor"
+ program_icon = "plug"
var/has_alert = 0
var/obj/structure/cable/attached_wire
diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm
index afafa9f90706..2404f821de40 100644
--- a/code/modules/modular_computers/file_system/programs/radar.dm
+++ b/code/modules/modular_computers/file_system/programs/radar.dm
@@ -1,12 +1,13 @@
/datum/computer_file/program/radar //generic parent that handles most of the process
filename = "genericfinder"
filedesc = "debug_finder"
+ category = PROGRAM_CATEGORY_CREW
ui_header = "borg_mon.gif" //DEBUG -- new icon before PR
- program_icon_state = "generic"
- extended_desc = "generic"
+ program_icon_state = "radarntos"
requires_ntnet = TRUE
transfer_access = null
available_on_ntnet = FALSE
+ usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET | PROGRAM_PHONE
network_destination = "tracking program"
size = 5
tgui_id = "NtosRadar"
@@ -17,10 +18,28 @@
var/atom/selected
///Used to store when the next scan is available. Updated by the scan() proc.
var/next_scan = 0
+ ///Used to keep track of the last value program_icon_state was set to, to prevent constant unnecessary update_icon() calls
+ var/last_icon_state = ""
+ ///Used by the tgui interface, themed NT or Syndicate.
+ var/arrowstyle = "ntosradarpointer.png"
+ ///Used by the tgui interface, themed for NT or Syndicate colors.
+ var/pointercolor = "green"
+
+/datum/computer_file/program/radar/run_program(mob/living/user)
+ . = ..()
+ if(.)
+ START_PROCESSING(SSfastprocess, src)
+ return
+ return FALSE
/datum/computer_file/program/radar/kill_program(forced = FALSE)
objects = list()
selected = null
+ STOP_PROCESSING(SSfastprocess, src)
+ return ..()
+
+/datum/computer_file/program/radar/Destroy()
+ STOP_PROCESSING(SSfastprocess, src)
return ..()
/datum/computer_file/program/radar/Destroy()
@@ -70,7 +89,37 @@
*
*/
/datum/computer_file/program/radar/proc/track()
- return
+ var/atom/movable/signal = find_atom()
+ if(!trackable(signal))
+ return
+
+ var/turf/here_turf = (get_turf(computer))
+ var/turf/target_turf = (get_turf(signal))
+ var/userot = FALSE
+ var/rot = 0
+ var/pointer="crosshairs"
+ var/locx = (target_turf.x - here_turf.x) + 24
+ var/locy = (here_turf.y - target_turf.y) + 24
+
+ if(get_dist_euclidian(here_turf, target_turf) > 24)
+ userot = TRUE
+ rot = round(Get_Angle(here_turf, target_turf))
+ else
+ if(target_turf.z > here_turf.z)
+ pointer="caret-up"
+ else if(target_turf.z < here_turf.z)
+ pointer="caret-down"
+
+ var/list/trackinfo = list(
+ "locx" = locx,
+ "locy" = locy,
+ "userot" = userot,
+ "rot" = rot,
+ "arrowstyle" = arrowstyle,
+ "color" = pointercolor,
+ "pointer" = pointer,
+ )
+ return trackinfo
/**
*
@@ -82,10 +131,12 @@
**arg1 is the atom being evaluated.
*/
/datum/computer_file/program/radar/proc/trackable(atom/movable/signal)
- if(!signal)
+ if(!signal || !computer)
return FALSE
var/turf/here = get_turf(computer)
var/turf/there = get_turf(signal)
+ if(!here || !there)
+ return FALSE //I was still getting a runtime even after the above check while scanning, so fuck it
return (there.z == here.z) || (is_station_level(here.z) && is_station_level(there.z))
/**
@@ -103,6 +154,59 @@
/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.
+*/
+/datum/computer_file/program/radar/proc/find_atom()
+ return
+
+//We use SSfastprocess for the program icon state because it runs faster than process_tick() does.
+/datum/computer_file/program/radar/process()
+ if(computer.active_program != src)
+ STOP_PROCESSING(SSfastprocess, src) //We're not the active program, it's time to stop.
+ return
+ if(!selected)
+ return
+
+ var/atom/movable/signal = find_atom()
+ if(!trackable(signal))
+ program_icon_state = "[initial(program_icon_state)]lost"
+ if(last_icon_state != program_icon_state)
+ computer.update_icon()
+ last_icon_state = program_icon_state
+ return
+
+ var/here_turf = get_turf(computer)
+ var/target_turf = get_turf(signal)
+ var/trackdistance = get_dist_euclidian(here_turf, target_turf)
+ switch(trackdistance)
+ if(0)
+ program_icon_state = "[initial(program_icon_state)]direct"
+ if(1 to 12)
+ program_icon_state = "[initial(program_icon_state)]close"
+ if(13 to 24)
+ program_icon_state = "[initial(program_icon_state)]medium"
+ if(25 to INFINITY)
+ program_icon_state = "[initial(program_icon_state)]far"
+
+ if(last_icon_state != program_icon_state)
+ computer.update_icon()
+ last_icon_state = program_icon_state
+ computer.setDir(get_dir(here_turf, target_turf))
+
+//We can use process_tick to restart fast processing, since the computer will be running this constantly either way.
+/datum/computer_file/program/radar/process_tick()
+ if(computer.active_program == src)
+ START_PROCESSING(SSfastprocess, src)
+
///////////////////
//Suit Sensor App//
///////////////////
@@ -111,44 +215,14 @@
/datum/computer_file/program/radar/lifeline
filename = "Lifeline"
filedesc = "Lifeline"
- program_icon_state = "generic"
extended_desc = "This program allows for tracking of crew members via their suit sensors."
requires_ntnet = TRUE
transfer_access = ACCESS_MEDICAL
available_on_ntnet = TRUE
+ program_icon = "heartbeat"
-/datum/computer_file/program/radar/lifeline/track()
- var/mob/living/carbon/human/humanoid = locate(selected) in GLOB.mob_living_list
- if(!istype(humanoid) || !trackable(humanoid))
- return
-
- var/turf/here_turf = (get_turf(computer))
- var/turf/target_turf = (get_turf(humanoid))
- var/userot = FALSE
- var/rot = 0
- var/pointer="crosshairs"
- var/locx = (target_turf.x - here_turf.x)
- var/locy = (here_turf.y - target_turf.y)
- if(get_dist_euclidian(here_turf, target_turf) > 24) //If they're too far away, we need the angle for the arrow along the edge of the radar display
- userot = TRUE
- rot = round(Get_Angle(here_turf, target_turf))
- else
- locx = locx + 24
- locy = locy + 24
- if(target_turf.z > here_turf.z)
- pointer="caret-up"
- else if(target_turf.z < here_turf.z)
- pointer="caret-down"
- var/list/trackinfo = list(
- locx = locx,
- locy = locy,
- userot = userot,
- rot = rot,
- arrowstyle = "ntosradarpointer.png", //For the rotation arrow, it's stupid I know
- color = "green",
- pointer = pointer,
- )
- return trackinfo
+/datum/computer_file/program/radar/lifeline/find_atom()
+ return locate(selected) in GLOB.carbon_list
/datum/computer_file/program/radar/lifeline/scan()
if(world.time < next_scan)
@@ -191,46 +265,19 @@
/datum/computer_file/program/radar/fission360
filename = "Fission360"
filedesc = "Fission360"
- program_icon_state = "generic"
+ category = PROGRAM_CATEGORY_MISC
+ program_icon_state = "radarsyndicate"
extended_desc = "This program allows for tracking of nuclear authorization disks and warheads."
requires_ntnet = FALSE
transfer_access = null
available_on_ntnet = FALSE
available_on_syndinet = TRUE
tgui_id = "NtosRadarSyndicate"
+ arrowstyle = "ntosradarpointerS.png"
+ pointercolor = "red"
-/datum/computer_file/program/radar/fission360/track()
- var/obj/nuke = locate(selected) in GLOB.poi_list
- if(!trackable(nuke))
- return
-
- var/turf/here_turf = (get_turf(computer))
- var/turf/target_turf = (get_turf(nuke))
- var/userot = FALSE
- var/rot = 0
- var/pointer="crosshairs"
- var/locx = (target_turf.x - here_turf.x)
- var/locy = (here_turf.y - target_turf.y)
- if(get_dist_euclidian(here_turf, target_turf) > 24) //If they're too far away, we need the angle for the arrow along the edge of the radar display
- userot = TRUE
- rot = round(Get_Angle(here_turf, target_turf))
- else
- locx = locx + 24
- locy = locy + 24
- if(target_turf.z > here_turf.z)
- pointer="caret-up"
- else if(target_turf.z < here_turf.z)
- pointer="caret-down"
- var/list/trackinfo = list(
- locx = locx,
- locy = locy,
- userot = userot,
- rot = rot,
- arrowstyle = "ntosradarpointerS.png",
- color = "red",
- pointer = pointer,
- )
- return trackinfo
+/datum/computer_file/program/radar/fission360/find_atom()
+ return locate(selected) in GLOB.poi_list
/datum/computer_file/program/radar/fission360/scan()
if(world.time < next_scan)
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 8644ce09b433..41fe1b574109 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -2,6 +2,7 @@
/datum/computer_file/program/robocontrol
filename = "robocontrol"
filedesc = "Bot Remote Controller"
+ category = PROGRAM_CATEGORY_ROBO
program_icon_state = "robot"
extended_desc = "A remote controller used for giving basic commands to non-sentient robots."
transfer_access = ACCESS_ROBOTICS
@@ -9,6 +10,8 @@
network_destination = "robotics control network"
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.
@@ -78,7 +81,7 @@
return
if(id_card)
GLOB.data_core.manifest_modify(id_card.registered_name, id_card.assignment)
- card_slot.try_eject(TRUE, current_user)
+ card_slot.try_eject(current_user)
else
playsound(get_turf(ui_host()) , 'sound/machines/buzz-sigh.ogg', 25, FALSE)
return
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 1264f4fb1547..f43d558d93a2 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -11,7 +11,7 @@
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
size = 10
tgui_id = "NtosSecurEye"
-// program_icon = "eye" //Yogs: We uh, dont have this var
+ program_icon = "eye"
var/list/network = list("ss13")
var/obj/machinery/camera/active_camera
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 1acfb54c7b2f..c49a0cd002f2 100644
--- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm
@@ -1,6 +1,7 @@
/datum/computer_file/program/supermatter_monitor
filename = "smmonitor"
filedesc = "Supermatter Monitoring"
+ category = PROGRAM_CATEGORY_ENGI
ui_header = "smmon_0.gif"
program_icon_state = "smmon_0"
extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines."
@@ -9,11 +10,18 @@
network_destination = "supermatter monitoring system"
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.
var/data_corrupted = FALSE //used for when supermatter corruptor is attached
+/datum/computer_file/program/supermatter_monitor/Destroy()
+ clear_signals()
+ active = null
+ return ..()
/datum/computer_file/program/supermatter_monitor/process_tick()
..()
@@ -27,10 +35,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
..()
@@ -54,6 +63,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()
@@ -132,6 +193,7 @@
switch(action)
if("PRG_clear")
+ clear_signals()
active = null
return TRUE
if("PRG_refresh")
@@ -142,4 +204,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 bc40f6e80c76..29da37e09948 100644
--- a/code/modules/modular_computers/hardware/_hardware.dm
+++ b/code/modules/modular_computers/hardware/_hardware.dm
@@ -10,9 +10,11 @@
// Computer that holds this hardware, if any.
var/power_usage = 0 // If the hardware uses extra power, change this.
- var/enabled = 1 // If the hardware is turned off set this to 0.
- var/critical = 0 // Prevent disabling for important component, like the CPU.
- var/can_install = 1 // Prevents direct installation of removable media.
+ var/enabled = TRUE // If the hardware is turned off set this to 0.
+ var/critical = FALSE // Prevent disabling for important component, like the CPU.
+ var/can_install = TRUE // Prevents direct installation of removable media.
+ var/expansion_hw = FALSE // Hardware that fits into expansion bays.
+ var/removable = TRUE // Whether the hardware is removable or not.
var/damage = 0 // Current damage level
var/max_damage = 100 // Maximal damage level.
var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things
diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm
index 6ff87c7890ca..280bb7a69b70 100644
--- a/code/modules/modular_computers/hardware/ai_slot.dm
+++ b/code/modules/modular_computers/hardware/ai_slot.dm
@@ -5,6 +5,7 @@
icon_state = "card_mini"
w_class = WEIGHT_CLASS_NORMAL
device_type = MC_AI
+ expansion_hw = TRUE
var/obj/item/aicard/stored_card = null
var/locked = FALSE
@@ -19,12 +20,6 @@
if(stored_card)
. += "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it."
-/obj/item/computer_hardware/ai_slot/on_install(obj/item/modular_computer/M, mob/living/user = null)
- M.add_verb(device_type)
-
-/obj/item/computer_hardware/ai_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- M.remove_verb(device_type)
-
/obj/item/computer_hardware/ai_slot/try_insert(obj/item/I, mob/living/user = null)
if(!holder)
return FALSE
@@ -44,7 +39,7 @@
return TRUE
-/obj/item/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0)
+/obj/item/computer_hardware/ai_slot/try_eject(mob/living/user = null,forced = FALSE)
if(!stored_card)
to_chat(user, span_warning("There is no card in \the [src]."))
return FALSE
diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm
index 4a431391680d..f30e598a134b 100644
--- a/code/modules/modular_computers/hardware/battery_module.dm
+++ b/code/modules/modular_computers/hardware/battery_module.dm
@@ -12,9 +12,13 @@
battery = new battery_type(src)
..()
+/obj/item/computer_hardware/battery/Destroy()
+ . = ..()
+ QDEL_NULL(battery)
+
/obj/item/computer_hardware/battery/handle_atom_del(atom/A)
if(A == battery)
- try_eject(0, null, TRUE)
+ try_eject(forced = TRUE)
. = ..()
/obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null)
@@ -41,7 +45,7 @@
return TRUE
-/obj/item/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0)
+/obj/item/computer_hardware/battery/try_eject(mob/living/user = null, forced = FALSE)
if(!battery)
to_chat(user, span_warning("There is no power cell connected to \the [src]."))
return FALSE
diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm
index 94c6960801d1..8dd5ad0271c5 100644
--- a/code/modules/modular_computers/hardware/card_slot.dm
+++ b/code/modules/modular_computers/hardware/card_slot.dm
@@ -7,13 +7,10 @@
device_type = MC_CARD
var/obj/item/card/id/stored_card = null
- var/obj/item/card/id/stored_card2 = null
/obj/item/computer_hardware/card_slot/handle_atom_del(atom/A)
if(A == stored_card)
- try_eject(1, null, TRUE)
- if(A == stored_card2)
- try_eject(2, null, TRUE)
+ try_eject(null, TRUE)
. = ..()
/obj/item/computer_hardware/card_slot/Destroy()
@@ -21,26 +18,25 @@
return ..()
/obj/item/computer_hardware/card_slot/GetAccess()
- if(stored_card && stored_card2) // Best of both worlds
- return (stored_card.GetAccess() | stored_card2.GetAccess())
- else if(stored_card)
- return stored_card.GetAccess()
- else if(stored_card2)
- return stored_card2.GetAccess()
- return ..()
+ var/list/total_access
+ if(stored_card)
+ total_access = stored_card.GetAccess()
+ var/obj/item/computer_hardware/card_slot/card_slot2 = holder?.all_components[MC_CARD2] //Best of both worlds
+ if(card_slot2?.stored_card)
+ total_access |= card_slot2.stored_card.GetAccess()
+ return total_access
/obj/item/computer_hardware/card_slot/GetID()
if(stored_card)
return stored_card
- else if(stored_card2)
- return stored_card2
return ..()
-/obj/item/computer_hardware/card_slot/on_install(obj/item/modular_computer/M, mob/living/user = null)
- M.add_verb(device_type)
-
-/obj/item/computer_hardware/card_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- M.remove_verb(device_type)
+/obj/item/computer_hardware/card_slot/RemoveID()
+ if(stored_card)
+ . = stored_card
+ if(!try_eject())
+ return null
+ return
/obj/item/computer_hardware/card_slot/try_insert(obj/item/I, mob/living/user = null)
if(!holder)
@@ -49,8 +45,7 @@
if(!istype(I, /obj/item/card/id))
return FALSE
- if(stored_card && stored_card2)
- to_chat(user, span_warning("You try to insert \the [I] into \the [src], but its slots are occupied."))
+ if(stored_card)
return FALSE
if(user)
if(!user.transferItemToLoc(I, src))
@@ -58,11 +53,8 @@
else
I.forceMove(src)
- if(!stored_card)
- stored_card = I
- else
- stored_card2 = I
- to_chat(user, span_notice("You insert \the [I] into \the [src]."))
+ stored_card = I
+ to_chat(user, "You insert \the [I] into \the [expansion_hw ? "secondary":"primary"] [src].")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
if(ishuman(user))
var/mob/living/carbon/human/H = user
@@ -71,53 +63,60 @@
return TRUE
-/obj/item/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0)
- if(!stored_card && !stored_card2)
+/obj/item/computer_hardware/card_slot/try_eject(mob/living/user = null, forced = FALSE)
+ if(!stored_card)
to_chat(user, span_warning("There are no cards in \the [src]."))
return FALSE
- var/ejected = 0
- if(stored_card && (!slot || slot == 1))
- if(user && Adjacent(user))
- user.put_in_hands(stored_card)
- else
- stored_card.forceMove(drop_location())
- stored_card = null
- ejected++
-
- if(stored_card2 && (!slot || slot == 2))
- if(user && Adjacent(user))
- user.put_in_hands(stored_card2)
- else
- stored_card2.forceMove(drop_location())
- stored_card2 = null
- ejected++
-
- if(ejected)
- if(holder)
- if(holder.active_program)
- holder.active_program.event_idremoved(0, slot)
-
- for(var/I in holder.idle_threads)
- var/datum/computer_file/program/P = I
- P.event_idremoved(1, slot)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.sec_hud_set_ID()
- to_chat(user, span_notice("You remove the card[ejected>1 ? "s" : ""] from \the [src]."))
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
- return TRUE
- return FALSE
+ if(user)
+ user.put_in_hands(stored_card)
+ else
+ stored_card.forceMove(drop_location())
+ stored_card = null
+
+ if(holder)
+ if(holder.active_program)
+ holder.active_program.event_idremoved(0)
+
+ for(var/p in holder.idle_threads)
+ var/datum/computer_file/program/computer_program = p
+ computer_program.event_idremoved(1)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_user = user
+ human_user.sec_hud_set_ID()
+ to_chat(user, "You remove the card from \the [src].")
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ return TRUE
/obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user)
if(..())
return
if(I.tool_behaviour == TOOL_SCREWDRIVER)
- to_chat(user, span_notice("You press down on the manual eject button with \the [I]."))
- try_eject(0,user)
- return
+ if(stored_card)
+ to_chat(user, "You press down on the manual eject button with \the [I].")
+ try_eject(user)
+ return
+ swap_slot()
+ 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.
+*/
+/obj/item/computer_hardware/card_slot/proc/swap_slot()
+ expansion_hw = !expansion_hw
+ if(expansion_hw)
+ device_type = MC_CARD2
+ else
+ device_type = MC_CARD
/obj/item/computer_hardware/card_slot/examine(mob/user)
. = ..()
- if(stored_card || stored_card2)
+ . += "The connector is set to fit into [expansion_hw ? "an expansion bay" : "a computer's primary ID bay"], but can be adjusted with a screwdriver."
+ if(stored_card)
. += "There appears to be something loaded in the card slots."
+
+/obj/item/computer_hardware/card_slot/secondary
+ name = "auxillary identification card authentication module" // \improper breaks the find_hardware_by_name proc
+ desc = "A secondary identification card authentication module, allowing this computer to write data on ID cards. Necessary for some programs to run properly."
+ 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 3fc7ae625489..5e03b709cba3 100644
--- a/code/modules/modular_computers/hardware/hard_drive.dm
+++ b/code/modules/modular_computers/hardware/hard_drive.dm
@@ -166,6 +166,22 @@
max_capacity = 70
var/datum/antagonist/traitor/traitor_data // Syndicate hard drive has the user's data baked directly into it on creation
+/obj/item/computer_hardware/hard_drive/small/syndicate/install_default_programs()
+ store_file(new/datum/computer_file/program/computerconfig(src))
+ store_file(new/datum/computer_file/program/ntnetdownload/emagged(src))
+ store_file(new/datum/computer_file/program/filemanager(src))
+
+/// For tablets given to nuke ops
+/obj/item/computer_hardware/hard_drive/small/nukeops
+ power_usage = 8
+ max_capacity = 70
+
+/obj/item/computer_hardware/hard_drive/small/nukeops/install_default_programs()
+ store_file(new/datum/computer_file/program/computerconfig(src))
+ store_file(new/datum/computer_file/program/ntnetdownload/syndicate(src)) // Syndicate version; automatic access to syndicate apps and no NT apps
+ store_file(new/datum/computer_file/program/filemanager(src))
+ store_file(new/datum/computer_file/program/radar/fission360(src)) //I am legitimately afraid if I don't do this, Ops players will think they just don't get a pinpointer anymore.
+
/obj/item/computer_hardware/hard_drive/micro
name = "micro solid state drive"
desc = "A highly efficient SSD chip for portable devices."
diff --git a/code/modules/modular_computers/hardware/portable_disk.dm b/code/modules/modular_computers/hardware/portable_disk.dm
index b5a957be04a8..89b0382e8662 100644
--- a/code/modules/modular_computers/hardware/portable_disk.dm
+++ b/code/modules/modular_computers/hardware/portable_disk.dm
@@ -8,12 +8,8 @@
max_capacity = 16
device_type = MC_SDD
-/obj/item/computer_hardware/hard_drive/portable/on_install(obj/item/modular_computer/M, mob/living/user = null)
- M.add_verb(device_type)
-
-/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/M, mob/living/user = null)
- ..()
- M.remove_verb(device_type)
+/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/MC, mob/user)
+ return //this is a floppy disk, let's not shut the computer down when it gets pulled out.
/obj/item/computer_hardware/hard_drive/portable/install_default_programs()
return // Empty by default
diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm
index 3ca2c1ad6246..c974a29be025 100644
--- a/code/modules/modular_computers/hardware/printer.dm
+++ b/code/modules/modular_computers/hardware/printer.dm
@@ -5,6 +5,7 @@
icon_state = "printer"
w_class = WEIGHT_CLASS_BULKY
device_type = MC_PRINT
+ expansion_hw = TRUE
var/stored_paper = 20
var/max_paper = 30
diff --git a/code/modules/modular_computers/hardware/sensor_package.dm b/code/modules/modular_computers/hardware/sensor_package.dm
new file mode 100644
index 000000000000..3c123ff18f56
--- /dev/null
+++ b/code/modules/modular_computers/hardware/sensor_package.dm
@@ -0,0 +1,8 @@
+//This item doesn't do much on its own, but is required by apps such as Atmospheric Scanner.
+/obj/item/computer_hardware/sensorpackage
+ name = "sensor package"
+ desc = "An integrated sensor package allowing a computer to take readings from the environment. Required by certain programs."
+ icon_state = "servo"
+ w_class = WEIGHT_CLASS_TINY
+ device_type = MC_SENSORS
+ expansion_hw = TRUE
diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm
index 9134579a26d4..89c517f2d370 100644
--- a/code/modules/modular_computers/laptop_vendor.dm
+++ b/code/modules/modular_computers/laptop_vendor.dm
@@ -53,6 +53,7 @@
var/obj/item/computer_hardware/battery/battery_module = null
if(fabricate)
fabricated_laptop = new /obj/item/modular_computer/laptop/buildable(src)
+ fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot)
fabricated_laptop.install_component(new /obj/item/computer_hardware/battery)
battery_module = fabricated_laptop.all_components[MC_CELL]
total_price = 49
@@ -108,7 +109,7 @@
if(dev_card)
total_price += 99
if(fabricate)
- fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot)
+ fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot/secondary)
return total_price
@@ -118,6 +119,7 @@
fabricated_tablet = new(src)
fabricated_tablet.install_component(new /obj/item/computer_hardware/battery)
fabricated_tablet.install_component(new /obj/item/computer_hardware/processor_unit/small)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot)
battery_module = fabricated_tablet.all_components[MC_CELL]
total_price = 99
switch(dev_battery)
@@ -156,11 +158,11 @@
if(dev_printer)
total_price += 49
if(fabricate)
- fabricated_tablet.install_component(new/obj/item/computer_hardware/printer)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/printer/mini)
if(dev_card)
- total_price += 99
+ total_price += 199
if(fabricate)
- fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot/secondary)
return total_price
else if(devtype == 3) // Phone, very portable and can function similar to the PDA, though it is even more expensive than a tablet.
@@ -205,13 +207,11 @@
fabricated_phone.install_component(new/obj/item/computer_hardware/network_card/advanced)
total_price += 149
if(dev_printer)
- total_price += 49
- if(fabricate)
- fabricated_phone.install_component(new/obj/item/computer_hardware/printer)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/printer/mini)
if(dev_card)
- total_price += 99
+ total_price += 199
if(fabricate)
- fabricated_phone.install_component(new/obj/item/computer_hardware/card_slot)
+ fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot/secondary)
return total_price
return 0
@@ -311,7 +311,7 @@
say("Insufficient money on card to purchase!")
return
credits += target_credits
- say("$[target_credits] has been desposited from your account.")
+ say("[target_credits] cr have been withdrawn from your account.")
return
return ..()
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 7f5f85424758..50211f228c99 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -567,6 +567,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
supermatter_anomaly_gen(src, PYRO_ANOMALY, rand(5, 10))
if(damage > warning_point) // while the core is still damaged and it's still worth noting its status
+ if(damage_archived < warning_point) //If damage_archive is under the warning point, this is the very first cycle that we've reached said point.
+ SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_START_ALARM)
if((REALTIMEOFDAY - lastwarning) / 10 >= WARNING_DELAY)
alarm()
@@ -575,6 +577,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
radio.talk_into(src, "[warning_alert] Integrity: [get_fake_integrity()]%!", common_channel)
else
radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%", common_channel)
+ SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM)
log_game("The supermatter crystal: [emergency_alert] Integrity: [get_integrity()]%") // yogs start - Logs SM chatter
investigate_log("The supermatter crystal: [emergency_alert] Integrity: [get_integrity()]%", INVESTIGATE_SUPERMATTER) // yogs end
lastwarning = REALTIMEOFDAY
@@ -587,6 +590,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
radio.talk_into(src, "[warning_alert] Integrity: [get_fake_integrity()]%!", engineering_channel)
else
radio.talk_into(src, "[warning_alert] Integrity: [get_integrity()]%", engineering_channel)
+ SEND_SIGNAL(src, COMSIG_SUPERMATTER_DELAM_ALARM)
log_game("The supermatter crystal: [warning_alert] Integrity: [get_integrity()]%") // yogs start - Logs SM chatter
investigate_log("The supermatter crystal: [warning_alert] Integrity: [get_integrity()]%", INVESTIGATE_SUPERMATTER) // yogs end
lastwarning = REALTIMEOFDAY - (WARNING_DELAY * 5)
diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm
index ab487b0aaa49..86121f56892f 100644
--- a/code/modules/research/designs/computer_part_designs.dm
+++ b/code/modules/research/designs/computer_part_designs.dm
@@ -122,6 +122,15 @@
category = list("Computer Parts")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+/datum/design/cardslot/secondary
+ name = "Auxilary ID Card Slot"
+ id = "secondcardslot"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron = 600)
+ build_path = /obj/item/computer_hardware/card_slot/secondary
+ category = list("Computer Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+
// Intellicard slot
/datum/design/aislot
name = "Intellicard Slot"
@@ -243,3 +252,12 @@
build_path = /obj/item/computer_hardware/processor_unit/photonic/small
category = list("Computer Parts")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+
+/datum/design/sensorpackage
+ name = "Sensor Package"
+ id = "sensorpackage"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron = 200, /datum/material/glass = 100, /datum/material/gold = 50, /datum/material/silver = 50)
+ build_path = /obj/item/computer_hardware/sensorpackage
+ category = list("Computer Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 7c829543af15..58dc1775bf1a 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -439,8 +439,8 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) //they are really shitty
export_price = 2000
design_ids = list("hdd_basic", "hdd_advanced", "hdd_super", "hdd_cluster", "ssd_small", "ssd_micro", "netcard_basic", "netcard_advanced", "netcard_wired",
- "portadrive_basic", "portadrive_advanced", "portadrive_super", "cardslot", "aislot", "miniprinter", "APClink", "bat_control", "bat_normal", "bat_advanced",
- "bat_super", "bat_micro", "bat_nano", "cpu_normal", "pcpu_normal", "cpu_small", "pcpu_small")
+ "portadrive_basic", "portadrive_advanced", "portadrive_super", "cardslot", "secondcardslot", "aislot", "miniprinter", "APClink", "bat_control", "bat_normal", "bat_advanced",
+ "bat_super", "bat_micro", "bat_nano", "cpu_normal", "pcpu_normal", "cpu_small", "pcpu_small", "sensorpackage")
/datum/techweb_node/computer_board_gaming
id = "computer_board_gaming"
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index 9393b9641fed..4353a93b3b90 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -67,18 +67,20 @@
* public
*
* Open this UI (and initialize it with data).
+ *
+ * return bool - TRUE if a new pooled window is opened, FALSE in all other situations including if a new pooled window didn't open because one already exists.
*/
/datum/tgui/proc/open()
if(!user.client)
- return null
+ return FALSE
if(window)
- return null
+ return FALSE
process_status()
if(status < UI_UPDATE)
- return null
+ return FALSE
window = SStgui.request_pooled_window(user)
if(!window)
- return null
+ return FALSE
opened_at = world.time
window.acquire_lock(src)
if(!window.is_ready())
@@ -101,6 +103,8 @@
with_static_data = TRUE))
SStgui.on_open(src)
+ return TRUE
+
/**
* public
*
@@ -156,7 +160,7 @@
*/
/datum/tgui/proc/send_asset(datum/asset/asset)
if(!window)
- CRASH("send_asset() can only be called after open().")
+ CRASH("send_asset() was called either without calling open() first or when open() did not return TRUE.")
return window.send_asset(asset)
/**
diff --git a/code/modules/vending/modularpc.dm b/code/modules/vending/modularpc.dm
index ee8bf6a24ca6..2dbdb6fcf9c2 100644
--- a/code/modules/vending/modularpc.dm
+++ b/code/modules/vending/modularpc.dm
@@ -15,8 +15,10 @@
/obj/item/computer_hardware/battery = 8,
/obj/item/stock_parts/cell/computer = 8,
/obj/item/computer_hardware/processor_unit = 4,
- /obj/item/computer_hardware/processor_unit/small = 4)
- premium = list(/obj/item/computer_hardware/card_slot = 2,
+ /obj/item/computer_hardware/processor_unit/small = 4,
+ /obj/item/computer_hardware/card_slot = 4,
+ /obj/item/computer_hardware/sensorpackage = 4)
+ premium = list(/obj/item/computer_hardware/card_slot/secondary = 2,
/obj/item/computer_hardware/ai_slot = 2,
/obj/item/computer_hardware/printer/mini = 2,
/obj/item/computer_hardware/recharger/APC = 2)
diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi
index 2f40b4ad6d5b..3030b1e5dde5 100644
Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ
diff --git a/icons/obj/modular_phone.dmi b/icons/obj/modular_phone.dmi
index 2e62b3cff54c..8cd68430cf13 100644
Binary files a/icons/obj/modular_phone.dmi and b/icons/obj/modular_phone.dmi differ
diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi
index 489a50003055..88c07473fdc3 100644
Binary files a/icons/obj/modular_tablet.dmi and b/icons/obj/modular_tablet.dmi differ
diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator.js b/tgui/packages/tgui/interfaces/ComputerFabricator.js
index 823b9ecc471b..21309df30321 100644
--- a/tgui/packages/tgui/interfaces/ComputerFabricator.js
+++ b/tgui/packages/tgui/interfaces/ComputerFabricator.js
@@ -254,13 +254,14 @@ const CfStep2 = (props, context) => {
- Card Reader:
+ Secondary Card Reader:
diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js
index ce562f52366a..03285c5c6c44 100644
--- a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js
+++ b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js
@@ -6,6 +6,7 @@ import { NtosWindow } from '../layouts';
export const NtosCyborgRemoteMonitor = (props, context) => {
return (
diff --git a/tgui/packages/tgui/interfaces/NtosFileManager.js b/tgui/packages/tgui/interfaces/NtosFileManager.js
index da762e459f42..f093b3147a8b 100644
--- a/tgui/packages/tgui/interfaces/NtosFileManager.js
+++ b/tgui/packages/tgui/interfaces/NtosFileManager.js
@@ -24,7 +24,8 @@ export const NtosFileManager = (props, context) => {
name: file,
new_name: newName,
})}
- onDuplicate={file => act('PRG_clone', { file: file })} />
+ onDuplicate={file => act('PRG_clone', { file: file })}
+ onToggleSilence={file => act('PRG_togglesilence', { name: file })} />
{usbconnected && (
@@ -54,6 +55,7 @@ const FileTable = props => {
onUpload,
onDelete,
onRename,
+ onToggleSilence,
} = props;
return (
@@ -89,6 +91,13 @@ const FileTable = props => {
{file.size}
+ {!!file.alert_able && (
+
)}
+ act('PC_Eject_Disk', { name: "ID" })}
+ />
+ )}>
+
+
+ ID Name: {login.IDName}
+
+
+ Assignment: {login.IDJob}
+
+
+
+ {!!removable_media.length && (
+
+
+ {removable_media.map(device => (
+
+
+
+
+ ))}
+
+
+ )}
{programs.map(program => (
@@ -63,9 +85,8 @@ export const NtosMain = (props, context) => {