[MDB Ignore] Re-add hop console second ID slot (#92157)

## About The Pull Request

<img width="491" height="301" alt="image"
src="https://github.com/user-attachments/assets/a3b5b19f-edf5-4de9-9201-9cbfab9e8827"
/>

Mod computers with the access changing software installed have a
secondary ID slot once again. This ID slot doesn't contribute to access.
You can insert IDs into the slot with right click and remove them with
alt-right click.

Also removes the "New IDs and you" memo paper. 

Also tweaks PDA on_deconstruct so contents are dropped on when they're
deconstructed with assembly.

Fixes #92151

## Why It's Good For The Game

Changing IDs is very unnecessarily clunky with the one slot. Insert hop
id, log in, remove hop id, insert crew id, change access, remove crew
id, log out.

We had it right back when we had two slots. Insert hop ID, insert crew
id, log in. It just works.

This also allows for mobile HoPs to change access without necessitating
removing their ID from their PDA.

Other changes: 

The "New IDs and you" memo is very old. They haven't been new for 4
years now. I don't think anyone reads it and they served their purpose.

I found it odd that, if your PDA was melted or blown up, it would delete
your ID. If this is a hold-over from old PDA behavior feel free to let
me know but otherwise it seems sensible that it'd spit out the contents
as you would expect.

## Changelog

🆑 Melbert
qol: The access changing software (the HoP console) now has ID two slots
again (one for the HoP's id and one for the ID being changed). You can
insert IDs in the secondary slot via the UI or right click, and remove
them via the UI or alt-right click.
qol: If your PDA is destroyed via acid or bombs, your ID (and similar
contents such as disks) are spit out instead of being deleted
del: Deletes the "New IDs and you" memo in the HoP's office. They
haven't been new for 4 years.
fix: Engineering sub-tab in the access changing software no longer looks
messed up
fix: Fix reversed alt-click logic for mod pcs
/🆑
This commit is contained in:
MrMelbert
2025-08-11 14:08:18 -05:00
committed by GitHub
parent 57af2f13c1
commit 5df4de3f71
35 changed files with 3270 additions and 3125 deletions
@@ -15,7 +15,9 @@
interaction_flags_mouse_drop = NEED_HANDS | ALLOW_RESTING
///The ID currently stored in the computer.
var/obj/item/card/id/computer_id_slot
var/obj/item/card/id/stored_id
///The alt slot, only used by certain UIs like the access app.
var/obj/item/card/id/alt_stored_id
///The disk in this PDA. If set, this will be inserted on Initialize.
var/obj/item/computer_disk/inserted_disk
///The power cell the computer uses to run on.
@@ -189,8 +191,9 @@
QDEL_NULL(inserted_disk)
if(istype(inserted_pai))
QDEL_NULL(inserted_pai)
if(computer_id_slot)
QDEL_NULL(computer_id_slot)
QDEL_NULL(stored_id)
QDEL_NULL(alt_stored_id)
shell = null
physical = null
@@ -230,7 +233,7 @@
if(issilicon(user))
return NONE
if(RemoveID(user))
if(remove_id(user))
return CLICK_ACTION_SUCCESS
if(istype(inserted_pai)) // Remove pAI
@@ -239,22 +242,31 @@
return CLICK_ACTION_BLOCKING
/obj/item/modular_computer/click_alt_secondary(mob/user)
if(issilicon(user))
return NONE
if(remove_secondary_id(user))
return CLICK_ACTION_SUCCESS
return NONE
// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. //guess what
/obj/item/modular_computer/GetAccess()
if(computer_id_slot)
return computer_id_slot.GetAccess()
if(stored_id)
return stored_id.GetAccess()
return ..()
/obj/item/modular_computer/GetID()
if(computer_id_slot)
return computer_id_slot
if(stored_id)
return stored_id
return ..()
/obj/item/modular_computer/get_id_examine_strings(mob/user)
. = ..()
if(computer_id_slot)
. += "[src] is displaying [computer_id_slot]:"
. += computer_id_slot.get_id_examine_strings(user)
if(stored_id)
. += "[src] is displaying [stored_id]:"
. += stored_id.get_id_examine_strings(user)
/obj/item/modular_computer/proc/print_text(text_to_print, paper_title = "")
if(!stored_paper)
@@ -269,23 +281,23 @@
return TRUE
/**
* InsertID
* Attempt to insert the ID in either card slot, if ID is present - attempts swap
*
* Args:
* inserting_id - the ID being inserted
* user - The person inserting the ID
*/
/obj/item/modular_computer/InsertID(obj/item/card/inserting_id, mob/user)
/obj/item/modular_computer/insert_id(obj/item/card/inserting_id, mob/user)
if(!isnull(user) && !user.transferItemToLoc(inserting_id, src))
return FALSE
else
inserting_id.forceMove(src)
if(!isnull(computer_id_slot))
RemoveID(user, silent = TRUE)
if(!isnull(stored_id))
remove_id(user, silent = TRUE)
computer_id_slot = inserting_id
stored_id = inserting_id
if(!isnull(user))
to_chat(user, span_notice("You insert \the [inserting_id] into the card slot."))
@@ -303,30 +315,79 @@
SEND_SIGNAL(src, COMSIG_MODULAR_COMPUTER_INSERTED_ID, inserting_id, user)
return TRUE
/**
* Attempts to insert a secondary ID card into the computer. If there is already a secondary ID card, attempts to swap
*
* Args:
* * secondary_id - The ID card to insert into the secondary slot.
* * user - The mob trying to insert the ID, if there is one.
*/
/obj/item/modular_computer/proc/insert_secondary_id(obj/item/card/id/secondary_id, mob/user)
if(!isnull(alt_stored_id))
remove_secondary_id(user, silent = TRUE)
if(!user.transferItemToLoc(secondary_id, src))
return FALSE
alt_stored_id = secondary_id
if(!isnull(user))
to_chat(user, span_notice("You insert \the [secondary_id] into the secondary card slot."))
balloon_alert(user, "inserted secondary ID")
playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
return TRUE
/**
* Removes the alt ID card from the computer, and puts it in loc's hand if it's a mob
*
* Args:
* * user - The mob trying to remove the ID, if there is one
* * silent - Boolean, determines whether fluff text would be printed
*/
/obj/item/modular_computer/proc/remove_secondary_id(mob/user, silent = FALSE)
if(!alt_stored_id)
return FALSE
if(!issilicon(user) && in_range(src, user))
user.put_in_hands(alt_stored_id)
else
alt_stored_id.forceMove(drop_location())
var/obj/item/lost_id = alt_stored_id
alt_stored_id = null
if(!silent && !isnull(user))
to_chat(user, span_notice("You remove \the [lost_id] from the secondary card slot."))
balloon_alert(user, "removed secondary ID")
playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
return TRUE
/**
* Removes the ID card from the computer, and puts it in loc's hand if it's a mob
* Args:
* user - The mob trying to remove the ID, if there is one
* silent - Boolean, determines whether fluff text would be printed
* * user - The mob trying to remove the ID, if there is one
* * silent - Boolean, determines whether fluff text would be printed
*/
/obj/item/modular_computer/RemoveID(mob/user, silent = FALSE)
if(!computer_id_slot)
/obj/item/modular_computer/remove_id(mob/user, silent = FALSE)
if(!stored_id)
return ..()
if(crew_manifest_update)
GLOB.manifest.modify(computer_id_slot.registered_name, computer_id_slot.assignment, computer_id_slot.get_trim_assignment())
GLOB.manifest.modify(stored_id.registered_name, stored_id.assignment, stored_id.get_trim_assignment())
if(user && !issilicon(user) && in_range(src, user))
user.put_in_hands(computer_id_slot)
user.put_in_hands(stored_id)
else
computer_id_slot.forceMove(drop_location())
stored_id.forceMove(drop_location())
computer_id_slot = null
var/obj/item/lost_id = stored_id
stored_id = null
if(!silent && !isnull(user))
to_chat(user, span_notice("You remove the card from the card slot."))
playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
to_chat(user, span_notice("You remove \the [lost_id] from the card slot."))
balloon_alert(user, "removed ID")
playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
if(ishuman(loc))
var/mob/living/carbon/human/human_wearer = loc
@@ -391,15 +452,20 @@
. += "It is upgraded with an experimental long-ranged network capabilities, picking up NTNet frequencies while further away."
. += span_notice("It has [max_capacity] GQ of storage capacity.")
if(computer_id_slot)
if(stored_id)
if(Adjacent(user))
. += "It has \the [computer_id_slot] card installed in its card slot."
. += "It has \the [stored_id] card inserted in its card slot.[alt_stored_id ? "" : " [span_info("Alt-click to eject it.")]"]"
else
. += "Its identification card slot is currently occupied."
. += span_info("Alt-click [src] to eject the identification card.")
if(alt_stored_id)
if(Adjacent(user))
. += "It has \the [alt_stored_id] card stored in its secondary card slot. [span_info("Alt-click to eject it.")]"
else
. += "Its secondary identification card slot is currently occupied."
if(internal_cell)
. += span_info("Right-click it with a screwdriver to eject the [internal_cell]")
. += span_info("Right-click it with a screwdriver to eject the [internal_cell].")
/obj/item/modular_computer/examine_more(mob/user)
. = ..()
@@ -415,8 +481,10 @@
/obj/item/modular_computer/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if(computer_id_slot && isidcard(held_item))
context[SCREENTIP_CONTEXT_LMB] = "Swap ID"
if(isidcard(held_item))
context[SCREENTIP_CONTEXT_LMB] = stored_id ? "Swap ID" : "Insert ID"
if(HAS_TRAIT(src, TRAIT_MODPC_TWO_ID_SLOTS))
context[SCREENTIP_CONTEXT_RMB] = alt_stored_id ? "Swap Secondary ID" : "Insert Secondary ID"
. = CONTEXTUAL_SCREENTIP_SET
if(held_item?.tool_behaviour == TOOL_SCREWDRIVER && internal_cell)
@@ -426,7 +494,10 @@
context[SCREENTIP_CONTEXT_RMB] = "Deconstruct"
. = CONTEXTUAL_SCREENTIP_SET
if(computer_id_slot) // ID get removed first before pAIs
if(alt_stored_id)
context[SCREENTIP_CONTEXT_ALT_RMB] = "Remove Secondary ID"
. = CONTEXTUAL_SCREENTIP_SET
if(stored_id) // ID get removed first before pAIs
context[SCREENTIP_CONTEXT_ALT_LMB] = "Remove ID"
. = CONTEXTUAL_SCREENTIP_SET
else if(inserted_pai)
@@ -457,12 +528,14 @@
internal_cell = null
if(enabled && !use_energy())
shutdown_computer()
if(computer_id_slot == gone)
computer_id_slot = null
if(stored_id == gone)
stored_id = null
update_slot_icon()
if(ishuman(loc))
var/mob/living/carbon/human/human_wearer = loc
human_wearer.update_ID_card()
if(alt_stored_id == gone)
alt_stored_id = null
if(inserted_pai == gone)
update_appearance(UPDATE_ICON)
if(inserted_disk == gone)
@@ -718,8 +791,8 @@
///Imprints name and job into the modular computer, and calls back to necessary functions.
///Acts as a replacement to directly setting the imprints fields. All fields are optional, the proc will try to fill in missing gaps.
/obj/item/modular_computer/proc/imprint_id(name = null, job_name = null)
saved_identification = name || computer_id_slot?.registered_name || saved_identification
saved_job = job_name || computer_id_slot?.assignment || saved_job
saved_identification = name || stored_id?.registered_name || saved_identification
saved_job = job_name || stored_id?.assignment || saved_job
SEND_SIGNAL(src, COMSIG_MODULAR_PDA_IMPRINT_UPDATED, saved_identification, saved_job)
UpdateDisplay()
@@ -823,9 +896,15 @@
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/item/modular_computer/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers)
if(isidcard(tool) && HAS_TRAIT(src, TRAIT_MODPC_TWO_ID_SLOTS))
return insert_secondary_id(tool, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
return item_interaction(user, tool, modifiers)
/obj/item/modular_computer/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(isidcard(tool))
return InsertID(tool, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
return insert_id(tool, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
if(iscash(tool))
return money_act(user, tool)
@@ -854,8 +933,10 @@
if(istype(tool, /obj/item/computer_disk))
return computer_disk_act(user, tool)
return NONE
/obj/item/modular_computer/proc/money_act(mob/user, obj/item/money)
var/obj/item/card/id/inserted_id = computer_id_slot?.GetID()
var/obj/item/card/id/inserted_id = stored_id?.GetID()
if(!inserted_id)
balloon_alert(user, "no ID!")
return ITEM_INTERACT_BLOCKING
@@ -934,16 +1015,16 @@
return ITEM_INTERACT_SUCCESS
/obj/item/modular_computer/atom_deconstruct(disassembled = TRUE)
var/atom/droploc = drop_location()
remove_pai()
eject_aicard()
if (disassembled)
internal_cell?.forceMove(drop_location())
computer_id_slot?.forceMove(drop_location())
inserted_disk?.forceMove(drop_location())
new /obj/item/stack/sheet/iron(drop_location(), steel_sheet_cost)
else
internal_cell?.forceMove(droploc)
stored_id?.forceMove(droploc)
alt_stored_id?.forceMove(droploc)
inserted_disk?.forceMove(droploc)
if (!disassembled)
physical.visible_message(span_notice("\The [src] breaks apart!"))
new /obj/item/stack/sheet/iron(drop_location(), round(steel_sheet_cost * 0.5))
new /obj/item/stack/sheet/iron(droploc, steel_sheet_cost * (disassembled ? 1 : 0.5))
relay_qdel()
// Ejects the inserted intellicard, if one exists. Used when the computer is deconstructed.
@@ -998,7 +1079,7 @@
if(SEC_LEVEL_RED) // all-hands-on-deck situations, everyone is responsible for combatting a threat
return ALERT_RELEVANCY_PERTINENT
if(SEC_LEVEL_BLUE) // suspected threat. security needs to be alert and possibly preparing for it, no further concerns
if(ACCESS_SECURITY in computer_id_slot?.access)
if(ACCESS_SECURITY in stored_id?.access)
return ALERT_RELEVANCY_PERTINENT
else
return ALERT_RELEVANCY_WARN
@@ -97,9 +97,9 @@
)
data["proposed_login"] = list(
IDInserted = computer_id_slot ? TRUE : FALSE,
IDName = computer_id_slot?.registered_name,
IDJob = computer_id_slot?.assignment,
IDInserted = stored_id ? TRUE : FALSE,
IDName = stored_id?.registered_name,
IDJob = stored_id?.assignment,
)
data["removable_media"] = list()
@@ -211,7 +211,7 @@
return TRUE
if("ID")
if(RemoveID(user))
if(remove_id(user))
playsound(src, 'sound/machines/card_slide.ogg', 50)
return TRUE
@@ -76,7 +76,7 @@
/obj/item/modular_computer/pda/update_overlays()
. = ..()
if(computer_id_slot)
if(stored_id)
. += mutable_appearance(overlays_icon, "id_overlay")
if(light_on)
. += mutable_appearance(overlays_icon, "light_overlay")
@@ -50,10 +50,16 @@
return ..()
/obj/machinery/modular_computer/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
. = NONE
if(isnull(held_item))
context[SCREENTIP_CONTEXT_RMB] = "Toggle processor interaction"
return CONTEXTUAL_SCREENTIP_SET
. |= CONTEXTUAL_SCREENTIP_SET
if(CPU_INTERACTABLE(user))
. |= cpu?.add_context(source, context, held_item, user)
return .
/obj/machinery/modular_computer/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
@@ -117,11 +123,17 @@
return update_icon(updates)
/obj/machinery/modular_computer/click_alt(mob/user)
if(CPU_INTERACTABLE(user) || !can_interact(user))
if(!CPU_INTERACTABLE(user) || !can_interact(user))
return NONE
cpu.click_alt(user)
return CLICK_ACTION_SUCCESS
/obj/machinery/modular_computer/click_alt_secondary(mob/user)
if(!CPU_INTERACTABLE(user) || !can_interact(user))
return NONE
cpu.click_alt_secondary(user)
return CLICK_ACTION_SUCCESS
//ATTACK HAND IGNORING PARENT RETURN VALUE
// On-click handling. Turns on the computer if it's off and opens the GUI.
/obj/machinery/modular_computer/interact(mob/user)
@@ -157,6 +169,9 @@
/obj/machinery/modular_computer/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
return (CPU_INTERACTABLE(user) && !user.combat_mode) ? cpu.item_interaction(user, tool, modifiers) : ..()
/obj/machinery/modular_computer/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers)
return (CPU_INTERACTABLE(user) && !user.combat_mode) ? cpu.item_interaction_secondary(user, tool, modifiers) : ..()
/obj/machinery/modular_computer/attacked_by(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
return CPU_INTERACTABLE(user) ? cpu.attacked_by(attacking_item, user, modifiers, attack_modifiers) : ..()
@@ -173,7 +173,7 @@
if(!length(access))
var/obj/item/card/id/accesscard
if(computer)
accesscard = computer.computer_id_slot?.GetID()
accesscard = computer.stored_id?.GetID()
if(!accesscard)
if(loud && user)
@@ -202,7 +202,7 @@
if(!can_run(user, loud = TRUE))
return FALSE
if(program_flags & PROGRAM_REQUIRES_NTNET)
var/obj/item/card/id/ID = computer.computer_id_slot?.GetID()
var/obj/item/card/id/ID = computer.stored_id?.GetID()
generate_network_log("Connection opened -- Program ID:[filename] User:[ID?"[ID.registered_name]":"None"]")
SEND_SIGNAL(src, COMSIG_COMPUTER_PROGRAM_START, user)
return TRUE
@@ -228,7 +228,7 @@
return FALSE
if(program_flags & PROGRAM_REQUIRES_NTNET)
var/obj/item/card/id/ID = computer.computer_id_slot?.GetID()
var/obj/item/card/id/ID = computer.stored_id?.GetID()
generate_network_log("Connection closed -- Program ID: [filename] User:[ID ? "[ID.registered_name]" : "None"]")
computer.update_appearance(UPDATE_ICON)
@@ -40,17 +40,17 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
"description" = bets.description,
"owner" = bets == created_bet,
"creator" = bets.bet_owner,
"current_bets" = bets.get_bets(computer.computer_id_slot?.registered_account),
"current_bets" = bets.get_bets(computer.stored_id?.registered_account),
"locked" = bets.locked,
))
data["can_create_bet"] = !!isnull(created_bet)
if(isnull(computer.computer_id_slot))
if(isnull(computer.stored_id))
data["bank_name"] = null
data["bank_money"] = null
else
data["bank_name"] = computer.computer_id_slot.registered_account.account_holder
data["bank_money"] = computer.computer_id_slot.registered_account.account_balance
data["bank_name"] = computer.stored_id.registered_account.account_holder
data["bank_money"] = computer.stored_id.registered_account.account_balance
return data
@@ -63,7 +63,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
/datum/computer_file/program/betting/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/user = ui.user
if(isnull(computer.computer_id_slot))
if(isnull(computer.stored_id))
to_chat(user, span_danger("\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning."))
return
switch(action)
@@ -102,14 +102,14 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
var/option = params["option_selected"]
if(isnull(bet_placed_on))
return
bet_placed_on.bet_money(computer.computer_id_slot.registered_account, money_betting, option)
bet_placed_on.bet_money(computer.stored_id.registered_account, money_betting, option)
return TRUE
if("cancel_bet")
var/datum/active_bet/bet_cancelling
for(var/datum/active_bet/bets as anything in GLOB.active_bets)
if(bets.name == params["bet_selected"])
bet_cancelling = bets
bet_cancelling.cancel_bet(computer.computer_id_slot.registered_account)
bet_cancelling.cancel_bet(computer.stored_id.registered_account)
return TRUE
if("select_winner")
var/datum/active_bet/bets_ending
@@ -39,8 +39,8 @@
if(!networked)
GLOB.allbountyboards += computer
networked = TRUE
if(computer.computer_id_slot)
current_user = computer.computer_id_slot?.registered_account
if(computer.stored_id)
current_user = computer.stored_id?.registered_account
for(var/i in GLOB.request_list)
if(!i)
continue
@@ -48,7 +48,7 @@
//Aquire access from the inserted ID card.
if(!length(access))
var/obj/item/card/id/D = computer?.computer_id_slot?.GetID()
var/obj/item/card/id/D = computer?.stored_id?.GetID()
if(!D)
return FALSE
access = D.GetAccess()
@@ -63,7 +63,7 @@
data["location"] = SSshuttle.supply.getStatusText()
data["department"] = "Cargo"
var/datum/bank_account/buyer = SSeconomy.get_dep_account(cargo_account)
var/obj/item/card/id/id_card = computer.computer_id_slot?.GetID()
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
if(id_card?.registered_account)
if((ACCESS_COMMAND in id_card.access))
requestonly = FALSE
@@ -109,7 +109,6 @@
))
//Data regarding the User's capability to buy things.
data["has_id"] = id_card
data["away"] = SSshuttle.supply.getDockedId() == docking_away
data["self_paid"] = self_paid
data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE
@@ -244,7 +243,7 @@
return
var/reason = ""
if((requestonly && !self_paid) || !(computer.computer_id_slot?.GetID()))
if((requestonly && !self_paid) || !(computer.stored_id?.GetID()))
reason = tgui_input_text(usr, "Reason", name, max_length = MAX_MESSAGE_LEN)
if(isnull(reason) || ..())
return
@@ -260,13 +259,13 @@
return
if(!requestonly && !self_paid && ishuman(usr) && !account)
var/obj/item/card/id/id_card = computer.computer_id_slot?.GetID()
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department)
var/turf/T = get_turf(computer)
var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account)
SO.generateRequisition(T)
if((requestonly && !self_paid) || !(computer.computer_id_slot?.GetID()))
if((requestonly && !self_paid) || !(computer.stored_id?.GetID()))
SSshuttle.request_list += SO
else
SSshuttle.shopping_list += SO
@@ -290,7 +289,7 @@
var/id = text2num(params["id"])
for(var/datum/supply_order/SO in SSshuttle.request_list)
if(SO.id == id)
var/obj/item/card/id/id_card = computer.computer_id_slot?.GetID()
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
if(id_card && id_card?.registered_account)
SO.paying_account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department)
SSshuttle.request_list -= SO
@@ -26,6 +26,15 @@
/// Which departments this program has access to. See region defines.
var/target_dept
/datum/computer_file/program/card_mod/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing)
. = ..()
ADD_TRAIT(computer_installing, TRAIT_MODPC_TWO_ID_SLOTS, REF(src))
/datum/computer_file/program/card_mod/Destroy()
if(computer)
REMOVE_TRAIT(computer, TRAIT_MODPC_TWO_ID_SLOTS, REF(src))
return ..()
/**
* Authenticates the program based on the specific ID card.
*
@@ -80,24 +89,25 @@
/datum/computer_file/program/card_mod/kill_program(mob/user)
computer.crew_manifest_update = FALSE
var/obj/item/card/id/inserted_auth_card = computer.computer_id_slot
if(inserted_auth_card)
GLOB.manifest.modify(inserted_auth_card.registered_name, inserted_auth_card.assignment, inserted_auth_card.get_trim_assignment())
var/obj/item/card/id/modified_id = computer.alt_stored_id
if(modified_id)
GLOB.manifest.modify(modified_id.registered_name, modified_id.assignment, modified_id.get_trim_assignment())
return ..()
/datum/computer_file/program/card_mod/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/user = usr
var/obj/item/card/id/inserted_auth_card = computer.computer_id_slot
var/obj/item/card/id/auth_card = computer.stored_id
var/obj/item/card/id/modified_id = computer.alt_stored_id
switch(action)
// Log in.
if("PRG_authenticate")
if(!computer || !inserted_auth_card)
if(!computer || !auth_card)
playsound(computer, 'sound/machines/terminal/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
if(authenticate(user, inserted_auth_card))
if(authenticate(user, auth_card))
playsound(computer, 'sound/machines/terminal/terminal_on.ogg', 50, FALSE)
return TRUE
// Log out.
@@ -108,71 +118,61 @@
return TRUE
// Print a report.
if("PRG_print")
if(!computer)
return TRUE
if(!authenticated_card)
if(!computer || !authenticated_card || !modified_id)
return TRUE
var/contents = {"<h4>Access Report</h4>
<u>Prepared By:</u> [authenticated_user]<br>
<u>For:</u> [inserted_auth_card.registered_name ? inserted_auth_card.registered_name : "Unregistered"]<br>
<u>For:</u> [modified_id.registered_name || "Unregistered"]<br>
<hr>
<u>Assignment:</u> [inserted_auth_card.assignment]<br>
<u>Assignment:</u> [modified_id.assignment]<br>
<u>Access:</u><br>
"}
var/list/known_access_rights = SSid_access.get_region_access_list(list(REGION_ALL_STATION))
for(var/A in inserted_auth_card.access)
for(var/A in modified_id.access)
if(A in known_access_rights)
contents += " [SSid_access.get_access_desc(A)]"
if(!computer.print_text(contents, "access report - [inserted_auth_card.registered_name ? inserted_auth_card.registered_name : "Unregistered"]"))
if(!computer.print_text(contents, "access report - [modified_id.registered_name || "Unregistered"]"))
to_chat(usr, span_notice("Printer is out of paper."))
return TRUE
else
playsound(computer, 'sound/machines/terminal/terminal_on.ogg', 50, FALSE)
computer.visible_message(span_notice("\The [computer] prints out a paper."))
return TRUE
if("PRG_eject_id")
if(inserted_auth_card)
return computer.RemoveID(usr)
else
var/obj/item/I = user.get_active_held_item()
if(isidcard(I))
return computer.InsertID(I, user)
return TRUE
// Used to fire someone. Wipes all access from their card and modifies their assignment.
if("PRG_terminate")
if(!computer || !authenticated_card)
return TRUE
if(minor)
if(!(inserted_auth_card.trim?.type in job_templates))
if(!(modified_id.trim?.type in job_templates))
to_chat(usr, span_notice("Software error: You do not have the necessary permissions to demote this card."))
return TRUE
// Set the new assignment then remove the trim.
inserted_auth_card.assignment = is_centcom ? "Fired" : "Demoted"
SSid_access.remove_trim_from_card(inserted_auth_card)
modified_id.assignment = is_centcom ? "Fired" : "Demoted"
SSid_access.remove_trim_from_card(modified_id)
playsound(computer, 'sound/machines/terminal/terminal_prompt_deny.ogg', 50, FALSE)
return TRUE
// Change ID card assigned name.
if("PRG_edit")
if(!computer || !authenticated_card || !inserted_auth_card)
if(!computer || !authenticated_card || !modified_id)
return TRUE
var/old_name = inserted_auth_card.registered_name
var/old_name = modified_id.registered_name
// Sanitize the name first. We're not using the full sanitize_name proc as ID cards can have a wider variety of things on them that
// would not pass as a formal character name, but would still be valid on an ID card created by a player.
var/new_name = sanitize(params["name"])
if(!new_name)
inserted_auth_card.registered_name = null
modified_id.registered_name = null
playsound(computer, SFX_TERMINAL_TYPE, 50, FALSE)
inserted_auth_card.update_label()
modified_id.update_label()
// We had a name before and now we have no name, so this will unassign the card and we update the icon.
if(old_name)
inserted_auth_card.update_icon()
modified_id.update_icon()
return TRUE
// However, we are going to reject bad names overall including names with invalid characters in them, while allowing numbers.
@@ -182,63 +182,63 @@
to_chat(usr, span_notice("Software error: The ID card rejected the new name as it contains prohibited characters."))
return TRUE
inserted_auth_card.registered_name = new_name
modified_id.registered_name = new_name
playsound(computer, SFX_TERMINAL_TYPE, 50, FALSE)
inserted_auth_card.update_label()
modified_id.update_label()
// Card wasn't assigned before and now it is, so update the icon accordingly.
if(!old_name)
inserted_auth_card.update_icon()
modified_id.update_icon()
return TRUE
// Change age
if("PRG_age")
if(!computer || !authenticated_card || !inserted_auth_card)
if(!computer || !authenticated_card || !modified_id)
return TRUE
var/new_age = params["id_age"]
if(!isnum(new_age))
stack_trace("[key_name(usr)] ([usr]) attempted to set invalid age \[[new_age]\] to [inserted_auth_card]")
stack_trace("[key_name(usr)] ([usr]) attempted to set invalid age \[[new_age]\] to [modified_id]")
return TRUE
inserted_auth_card.registered_age = new_age
modified_id.registered_age = new_age
playsound(computer, SFX_TERMINAL_TYPE, 50, FALSE)
return TRUE
// Change assignment
if("PRG_assign")
if(!computer || !authenticated_card || !inserted_auth_card)
if(!computer || !authenticated_card || !modified_id)
return TRUE
var/new_asignment = trim(sanitize(params["assignment"]), MAX_NAME_LEN)
inserted_auth_card.assignment = new_asignment
modified_id.assignment = new_asignment
playsound(computer, SFX_TERMINAL_TYPE, 50, FALSE)
inserted_auth_card.update_label()
modified_id.update_label()
return TRUE
// Add/remove access.
if("PRG_access")
if(!computer || !authenticated_card || !inserted_auth_card)
if(!computer || !authenticated_card || !modified_id)
return TRUE
playsound(computer, SFX_TERMINAL_TYPE, 50, FALSE)
var/access_type = params["access_target"]
var/try_wildcard = params["access_wildcard"]
if(!(access_type in valid_access))
stack_trace("[key_name(usr)] ([usr]) attempted to add invalid access \[[access_type]\] to [inserted_auth_card]")
stack_trace("[key_name(usr)] ([usr]) attempted to add invalid access \[[access_type]\] to [modified_id]")
return TRUE
if(access_type in inserted_auth_card.access)
inserted_auth_card.remove_access(list(access_type))
LOG_ID_ACCESS_CHANGE(user, inserted_auth_card, "removed [SSid_access.get_access_desc(access_type)]")
if(access_type in modified_id.access)
modified_id.remove_access(list(access_type))
LOG_ID_ACCESS_CHANGE(user, modified_id, "removed [SSid_access.get_access_desc(access_type)]")
return TRUE
if(!inserted_auth_card.add_access(list(access_type), try_wildcard))
if(!modified_id.add_access(list(access_type), try_wildcard))
to_chat(usr, span_notice("ID error: ID card rejected your attempted access modification."))
LOG_ID_ACCESS_CHANGE(user, inserted_auth_card, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]")
LOG_ID_ACCESS_CHANGE(user, modified_id, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]")
return TRUE
if(access_type in ACCESS_ALERT_ADMINS)
message_admins("[ADMIN_LOOKUPFLW(user)] just added [SSid_access.get_access_desc(access_type)] to an ID card [ADMIN_VV(inserted_auth_card)] [(inserted_auth_card.registered_name) ? "belonging to [inserted_auth_card.registered_name]." : "with no registered name."]")
LOG_ID_ACCESS_CHANGE(user, inserted_auth_card, "added [SSid_access.get_access_desc(access_type)]")
message_admins("[ADMIN_LOOKUPFLW(user)] just added [SSid_access.get_access_desc(access_type)] to an ID card [ADMIN_VV(modified_id)] [(modified_id.registered_name) ? "belonging to [modified_id.registered_name]." : "with no registered name."]")
LOG_ID_ACCESS_CHANGE(user, modified_id, "added [SSid_access.get_access_desc(access_type)]")
return TRUE
// Apply template to ID card.
if("PRG_template")
if(!computer || !authenticated_card || !inserted_auth_card)
if(!computer || !authenticated_card || !modified_id)
return TRUE
playsound(computer, SFX_TERMINAL_TYPE, 50, FALSE)
@@ -252,18 +252,36 @@
if(trim.assignment != template_name)
continue
SSid_access.add_trim_access_to_card(inserted_auth_card, trim_path)
SSid_access.add_trim_access_to_card(modified_id, trim_path)
return TRUE
stack_trace("[key_name(usr)] ([usr]) attempted to apply invalid template \[[template_name]\] to [inserted_auth_card]")
stack_trace("[key_name(usr)] ([usr]) attempted to apply invalid template \[[template_name]\] to [modified_id]")
return TRUE
if("PRG_insert_main_id")
var/obj/item/card/id/main_id = user.get_active_held_item()
if(!isidcard(main_id))
return TRUE
computer.insert_id(main_id, user)
return TRUE
if("PRG_remove_main_id")
if(computer?.stored_id)
computer.remove_id(user)
return TRUE
if("PRG_insert_alt_id")
var/obj/item/card/id/alt_id = user.get_active_held_item()
if(!isidcard(alt_id))
to_chat(user, span_notice("You must hold an ID card to insert it into the secondary slot."))
return TRUE
computer.insert_secondary_id(alt_id, user)
return TRUE
if("PRG_remove_alt_id")
if(computer?.alt_stored_id)
computer.remove_secondary_id(user)
return TRUE
/datum/computer_file/program/card_mod/ui_static_data(mob/user)
var/list/data = list()
data["station_name"] = station_name()
data["centcom_access"] = is_centcom
data["minor"] = target_dept || minor ? TRUE : FALSE
var/list/regions = list()
var/list/tgui_region_data = SSid_access.all_region_access_tgui
@@ -277,11 +295,10 @@
data["regions"] = regions
data["accessFlags"] = SSid_access.flags_by_access
data["wildcardFlags"] = SSid_access.wildcard_flags_by_wildcard
data["accessFlagNames"] = SSid_access.access_flag_string_by_flag
data["showBasic"] = TRUE
data["access_flags"] = SSid_access.flags_by_access
data["wildcard_flags"] = SSid_access.wildcard_flags_by_wildcard
data["access_flag_names"] = SSid_access.access_flag_string_by_flag
data["show_basic"] = TRUE
data["templates"] = job_templates
return data
@@ -289,27 +306,31 @@
/datum/computer_file/program/card_mod/ui_data(mob/user)
var/list/data = list()
var/obj/item/card/id/inserted_id = computer.computer_id_slot
data["authIDName"] = inserted_id ? inserted_id.name : "-----"
data["authenticatedUser"] = authenticated_card
data["authed_user"] = authenticated_card
data["has_id"] = !!inserted_id
data["id_name"] = inserted_id ? inserted_id.name : "-----"
if(inserted_id)
data["id_rank"] = inserted_id.assignment ? inserted_id.assignment : "Unassigned"
data["id_owner"] = inserted_id.registered_name ? inserted_id.registered_name : "-----"
data["access_on_card"] = inserted_id.access
data["wildcardSlots"] = inserted_id.wildcard_slots
data["id_age"] = inserted_id.registered_age
var/obj/item/card/id/auth_id = computer.stored_id
var/obj/item/card/id/modified_id = computer.alt_stored_id
if(inserted_id.trim)
var/datum/id_trim/card_trim = inserted_id.trim
data["hasTrim"] = TRUE
data["trimAssignment"] = card_trim.assignment ? card_trim.assignment : ""
data["trimAccess"] = card_trim.access ? card_trim.access : list()
else
data["hasTrim"] = FALSE
data["trimAssignment"] = ""
data["trimAccess"] = list()
data["auth_card"] = auth_id ? get_id_ui_data(auth_id) : null
data["modified_card"] = modified_id ? get_id_ui_data(modified_id) : null
data["is_holding_id"] = isidcard(user.get_active_held_item())
return data
/datum/computer_file/program/card_mod/proc/get_id_ui_data(obj/item/card/id/card)
var/list/data = list()
var/datum/id_trim/card_trim = card.trim
data["id_name"] = card.name
data["id_rank"] = card.assignment || "Unassigned"
data["id_owner"] = card.registered_name || "-----"
data["access_on_card"] = card.access || list()
data["wildcard_slots"] = card.wildcard_slots || list()
data["id_age"] = card.registered_age
data["has_trim"] = !!card_trim
data["trim_assignment"] = card_trim?.assignment || ""
data["trim_access"] = card_trim?.access || list()
return data
@@ -19,26 +19,26 @@
/datum/computer_file/program/shipping/ui_data(mob/user)
var/list/data = list()
data["has_id_slot"] = !!computer.computer_id_slot
data["has_id_slot"] = !!computer.stored_id
data["paperamt"] = "[computer.stored_paper] / [computer.max_paper]"
data["card_owner"] = computer.computer_id_slot || "No Card Inserted."
data["card_owner"] = computer.stored_id || "No Card Inserted."
data["current_user"] = payments_acc ? payments_acc.account_holder : null
data["barcode_split"] = cut_multiplier * 100
return data
/datum/computer_file/program/shipping/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(!computer.computer_id_slot) //We need an ID to successfully run
if(!computer.stored_id) //We need an ID to successfully run
return FALSE
switch(action)
if("ejectid")
computer.RemoveID(usr)
computer.remove_id(usr)
if("selectid")
if(!computer.computer_id_slot.registered_account)
if(!computer.stored_id.registered_account)
playsound(get_turf(computer.ui_host()), 'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE, -1)
return TRUE
payments_acc = computer.computer_id_slot.registered_account
payments_acc = computer.stored_id.registered_account
playsound(get_turf(computer.ui_host()), 'sound/machines/ping.ogg', 50, TRUE, -1)
if("resetid")
payments_acc = null
@@ -30,7 +30,7 @@
data["printed_coupons"] = list()
data["redeemed_coupons"] = list()
data["valid_id"] = FALSE
var/obj/item/card/id/user_id = computer.computer_id_slot
var/obj/item/card/id/user_id = computer.stored_id
if(user_id?.registered_account.add_to_accounts)
for(var/datum/coupon_code/coupon as anything in user_id.registered_account.redeemed_coupons)
var/list/coupon_data = list(
@@ -46,7 +46,7 @@
/datum/computer_file/program/coupon/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/obj/item/card/id/user_id = computer.computer_id_slot
var/obj/item/card/id/user_id = computer.stored_id
if(!(user_id?.registered_account.add_to_accounts))
return TRUE
switch(action)
@@ -91,7 +91,7 @@
/datum/computer_file/program/coupon/tap(atom/tapped_atom, mob/living/user, list/modifiers)
if(!istype(tapped_atom, /obj/machinery/photocopier))
return FALSE
var/obj/item/card/id/user_id = computer.computer_id_slot
var/obj/item/card/id/user_id = computer.stored_id
if(!(user_id?.registered_account))
computer.balloon_alert(user, "no bank account found!")
return TRUE
@@ -64,7 +64,7 @@ GLOBAL_VAR(department_cd_override)
/datum/computer_file/program/department_order/ui_data(mob/user)
var/list/data = list()
data["no_link"] = !linked_department
data["id_inside"] = !!computer.computer_id_slot
data["id_inside"] = !!computer.stored_id
data["time_left"] = department_cooldowns[linked_department] ? DisplayTimeText(max(department_cooldowns[linked_department] - world.time, 0), 1) : null
data["can_override"] = !!department_order
return data
@@ -142,7 +142,7 @@ GLOBAL_VAR(department_cd_override)
if(!isnull(linked_department))
return TRUE
var/new_dept_type = find_department_to_link(computer.computer_id_slot)
var/new_dept_type = find_department_to_link(computer.stored_id)
if(isnull(new_dept_type))
computer.physical.balloon_alert(orderer, "no department found!")
playsound(computer, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
@@ -155,7 +155,7 @@ GLOBAL_VAR(department_cd_override)
if(isnull(linked_department))
return TRUE
var/obj/item/card/id/id_card = computer.computer_id_slot || orderer.get_idcard(hand_first = TRUE)
var/obj/item/card/id/id_card = computer.stored_id || orderer.get_idcard(hand_first = TRUE)
var/list/id_card_access = id_card?.GetAccess() || list()
if(length(use_access & id_card_access) <= 0)
@@ -55,7 +55,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/obj/item/card/id/user_id = computer.computer_id_slot
var/obj/item/card/id/user_id = computer.stored_id
if(!user_id || !(ACCESS_CHANGE_IDS in user_id.access))
return TRUE
@@ -107,7 +107,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
var/list/data = list()
var/authed = FALSE
var/obj/item/card/id/user_id = computer.computer_id_slot
var/obj/item/card/id/user_id = computer.stored_id
if(user_id && (ACCESS_CHANGE_IDS in user_id.access))
authed = TRUE
@@ -42,7 +42,7 @@
/datum/computer_file/program/nt_pay/ui_data(mob/user)
var/list/data = list()
current_user = computer.computer_id_slot?.registered_account || null
current_user = computer.stored_id?.registered_account || null
if(!current_user)
data["name"] = null
else
@@ -127,14 +127,14 @@
var/obj/item/modular_computer/modpc = associated_program.computer
RegisterSignal(modpc, COMSIG_MODULAR_COMPUTER_NT_PAY_RESULT, PROC_REF(on_payment_done))
RegisterSignal(modpc, COMSIG_MODULAR_COMPUTER_INSERTED_ID, PROC_REF(register_id))
if(modpc.computer_id_slot)
register_id(inserted_id = modpc.computer_id_slot)
if(modpc.stored_id)
register_id(inserted_id = modpc.stored_id)
/obj/item/circuit_component/mod_program/nt_pay/unregister_shell()
var/obj/item/modular_computer/modpc = associated_program.computer
UnregisterSignal(modpc, list(COMSIG_MODULAR_COMPUTER_NT_PAY_RESULT, COMSIG_MODULAR_COMPUTER_INSERTED_ID))
if(modpc.computer_id_slot)
UnregisterSignal(modpc.computer_id_slot, list(COMSIG_ID_CARD_NTPAY_MONEY_RECEIVED, COMSIG_MOVABLE_MOVED))
if(modpc.stored_id)
UnregisterSignal(modpc.stored_id, list(COMSIG_ID_CARD_NTPAY_MONEY_RECEIVED, COMSIG_MOVABLE_MOVED))
return ..()
/obj/item/circuit_component/mod_program/nt_pay/proc/register_id(datum/source, obj/item/card/inserted_id, mob/user)
@@ -28,14 +28,14 @@
var/list/mulelist = list()
if(computer)
data["id_owner"] = computer.computer_id_slot || ""
data["id_owner"] = computer.stored_id || ""
botcount = 0
for(var/mob/living/simple_animal/bot/simple_bot as anything in GLOB.bots_list)
if(!is_valid_z_level(current_turf, get_turf(simple_bot)) || !(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) //Only non-emagged bots on the same Z-level are detected!
continue
if(!simple_bot.allowed(user) && !simple_bot.check_access(computer.computer_id_slot)) // Only check Bots we can access
if(!simple_bot.allowed(user) && !simple_bot.check_access(computer.stored_id)) // Only check Bots we can access
continue
var/list/newbot = list(
"name" = simple_bot.name,
@@ -86,7 +86,7 @@
/datum/computer_file/program/robocontrol/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/current_user = ui.user
var/obj/item/card/id/id_card = computer?.computer_id_slot
var/obj/item/card/id/id_card = computer?.stored_id
var/static/list/standard_actions = list(
"patroloff",
@@ -116,15 +116,15 @@
if("summon")
simple_bot.bot_control(action, current_user, id_card ? id_card.access : id_card?.GetAccess())
if("ejectcard")
if(!computer || !computer.computer_id_slot)
if(!computer || !computer.stored_id)
return
if(id_card)
GLOB.manifest.modify(id_card.registered_name, id_card.assignment, id_card.get_trim_assignment())
computer.RemoveID(usr)
computer.remove_id(usr)
else
playsound(get_turf(computer.ui_host()) , 'sound/machines/buzz/buzz-sigh.ogg', 25, FALSE)
if("changedroneaccess")
if(!computer || !computer.computer_id_slot || !id_card)
if(!computer || !computer.stored_id || !id_card)
to_chat(current_user, span_notice("No ID found, authorization failed."))
return
if(isdrone(current_user))
@@ -111,7 +111,7 @@
if(computer.obj_flags & EMAGGED)
to_chat(usr, span_boldwarning("Security protocol error: Unable to access locking protocols."))
return TRUE
if(lock_access in computer?.computer_id_slot?.access)
if(lock_access in computer?.stored_id?.access)
locked = !locked
else
to_chat(usr, span_boldwarning("Unauthorized Access. Please insert research ID card."))