diff --git a/baystation12.dme b/baystation12.dme
index b2fadd2aaaf..699c8b87a3b 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1454,6 +1454,7 @@
#include "code\modules\modular_computers\file_system\program.dm"
#include "code\modules\modular_computers\file_system\programs\_engineering.dm"
#include "code\modules\modular_computers\file_system\programs\_medical.dm"
+#include "code\modules\modular_computers\file_system\programs\card.dm"
#include "code\modules\modular_computers\file_system\programs\configurator.dm"
#include "code\modules\modular_computers\file_system\programs\file_browser.dm"
#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm"
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
new file mode 100644
index 00000000000..1b992725fe5
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -0,0 +1,217 @@
+/datum/computer_file/program/card_mod
+ filename = "cardmod"
+ filedesc = "ID card modification program"
+ nanomodule_path = /datum/nano_module/card_mod
+ program_icon_state = "id"
+ extended_desc = "Program for programming employee ID cards to access parts of the station."
+ required_access = access_hop
+ requires_ntnet = 0
+ size = 8
+
+/datum/nano_module/card_mod
+ name = "ID card modification program"
+ var/mod_mode = 1
+ var/is_centcom = 0
+ var/show_assignments = 0
+
+/datum/nano_module/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
+ var/list/data = list()
+ if(program)
+ data = program.get_header_data()
+
+ data["src"] = "\ref[src]"
+ data["station_name"] = station_name()
+ data["manifest"] = data_core ? data_core.get_manifest(0) : null
+ data["assignments"] = show_assignments
+ if(program && program.computer)
+ data["have_id_slot"] = !!program.computer.card_slot
+ data["have_printer"] = !!program.computer.nano_printer
+ data["authenticated"] = program.can_run(user)
+ if(!program.computer.card_slot)
+ mod_mode = 0 //We can't modify IDs when there is no card reader
+ else
+ data["have_id_slot"] = 0
+ data["have_printer"] = 0
+ data["authenticated"] = 0
+ data["mmode"] = mod_mode
+ data["centcom_access"] = is_centcom
+
+ if(program && program.computer && program.computer.card_slot)
+ var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
+ data["has_id"] = !!id_card
+ data["id_account_number"] = id_card ? id_card.associated_account_number : null
+ data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned"
+ data["id_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----"
+ data["id_name"] = id_card ? id_card.name : "-----"
+
+
+ data["engineering_jobs"] = format_jobs(engineering_positions)
+ data["medical_jobs"] = format_jobs(medical_positions)
+ data["science_jobs"] = format_jobs(science_positions)
+ data["security_jobs"] = format_jobs(security_positions)
+ data["civilian_jobs"] = format_jobs(civilian_positions)
+ data["centcom_jobs"] = format_jobs(get_all_centcom_jobs())
+
+ data["all_centcom_access"] = is_centcom ? get_accesses(1) : null
+ data["regions"] = get_accesses()
+
+ if(program.computer.card_slot.stored_card)
+ var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
+ if(is_centcom)
+ var/list/all_centcom_access = list()
+ for(var/access in get_all_centcom_access())
+ all_centcom_access.Add(list(list(
+ "desc" = replacetext(get_centcom_access_desc(access), " ", " "),
+ "ref" = access,
+ "allowed" = (access in id_card.access) ? 1 : 0)))
+ data["all_centcom_access"] = all_centcom_access
+ else
+ var/list/regions = list()
+ for(var/i = 1; i <= 7; i++)
+ var/list/accesses = list()
+ for(var/access in get_region_accesses(i))
+ if (get_access_desc(access))
+ accesses.Add(list(list(
+ "desc" = replacetext(get_access_desc(access), " ", " "),
+ "ref" = access,
+ "allowed" = (access in id_card.access) ? 1 : 0)))
+
+ regions.Add(list(list(
+ "name" = get_region_accesses_name(i),
+ "accesses" = accesses)))
+ data["regions"] = regions
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "identification_computer.tmpl", name, 600, 700, state = state)
+ ui.auto_update_layout = 1
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/datum/nano_module/card_mod/proc/format_jobs(list/jobs)
+ var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
+ var/list/formatted = list()
+ for(var/job in jobs)
+ formatted.Add(list(list(
+ "display_name" = replacetext(job, " ", " "),
+ "target_rank" = id_card && id_card.assignment ? id_card.assignment : "Unassigned",
+ "job" = job)))
+
+ return formatted
+
+/datum/nano_module/card_mod/proc/get_accesses(var/is_centcom = 0)
+
+ return null
+
+
+/datum/computer_file/program/card_mod/Topic(href, href_list)
+ var/mob/living/user = usr
+ var/obj/item/weapon/card/id/user_id_card = user.GetIdCard()
+ var/obj/item/weapon/card/id/id_card = computer.card_slot.stored_card
+ var/datum/nano_module/card_mod/module = NM
+ switch(href_list["action"])
+ if("switchm")
+ if(href_list["target"] == "mod")
+ module.mod_mode = 1
+ else if (href_list["target"] == "manifest")
+ module.mod_mode = 0
+ if("togglea")
+ if(module.show_assignments)
+ module.show_assignments = 0
+ else
+ module.show_assignments = 1
+ if("print")
+ if(computer && computer.nano_printer) //This option should never be called if there is no printer
+ if(module.mod_mode)
+ if(can_run(user, 1))
+ var/contents = {"
Access Report
+ Prepared By: [user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
+ For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
+
+ Assignment: [id_card.assignment]
+ Account Number: #[id_card.associated_account_number]
+ Blood Type: [id_card.blood_type]
+ Access:
+ "}
+ for(var/A in id_card.access)
+ contents += " [get_access_desc(A)]"
+
+ if(!computer.nano_printer.print_text(contents,"access report"))
+ usr << "Hardware error: Printer was unable to print the file. It may be out of paper."
+ return
+ else
+ computer.visible_message("[computer] prints out paper.")
+ else
+ var/contents = {"Crew Manifest
+
+ [data_core ? data_core.get_manifest(0) : ""]
+ "}
+ if(!computer.nano_printer.print_text(contents,text("crew manifest ([])", worldtime2text())))
+ usr << "Hardware error: Printer was unable to print the file. It may be out of paper."
+ return
+ else
+ computer.visible_message("[computer] prints out paper.")
+ if("eject")
+ if(computer && computer.card_slot)
+ computer.proc_eject_id(user)
+ if("terminate")
+ if(computer && can_run(user, 1))
+ id_card.assignment = "Terminated"
+ id_card.access = list()
+ callHook("terminate_employee", list(id_card))
+ if("edit")
+ if(computer && can_run(user, 1))
+ if(href_list["name"])
+ var/temp_name = sanitizeName(input("Enter name.", "Name", id_card.registered_name))
+ if(temp_name)
+ id_card.registered_name = temp_name
+ else
+ computer.visible_message("[computer] buzzes rudely.")
+ else if(href_list["account"])
+ var/account_num = text2num(input("Enter account number.", "Account", id_card.associated_account_number))
+ id_card.associated_account_number = account_num
+ if("assign")
+ if(computer && can_run(user, 1) && id_card)
+ var/t1 = href_list["assign_target"]
+ if(t1 == "Custom")
+ var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45)
+ //let custom jobs function as an impromptu alt title, mainly for sechuds
+ if(temp_t)
+ id_card.assignment = temp_t
+ else
+ var/list/access = list()
+ if(module.is_centcom)
+ access = get_centcom_access(t1)
+ else
+ var/datum/job/jobdatum
+ for(var/jobtype in typesof(/datum/job))
+ var/datum/job/J = new jobtype
+ if(ckey(J.title) == ckey(t1))
+ jobdatum = J
+ break
+ if(!jobdatum)
+ usr << "No log exists for this job: [t1]"
+ return
+
+ access = jobdatum.get_access()
+
+ id_card.access = access
+ id_card.assignment = t1
+ id_card.rank = t1
+
+ callHook("reassign_employee", list(id_card))
+ if("access")
+ if(href_list["allowed"] && computer && can_run(user, 1))
+ var/access_type = text2num(href_list["access_target"])
+ var/access_allowed = text2num(href_list["allowed"])
+ if(access_type in (get_all_centcom_access() + get_all_station_access()))
+ id_card.access -= access_type
+ if(!access_allowed)
+ id_card.access += access_type
+ if(id_card)
+ id_card.name = text("[id_card.registered_name]'s ID Card ([id_card.assignment])")
+
+ nanomanager.update_uis(NM)
+ ..(href, href_list)
+ return 1
\ No newline at end of file
diff --git a/code/modules/modular_computers/hardware/nano_printer.dm b/code/modules/modular_computers/hardware/nano_printer.dm
index c3035740161..f179b9646a5 100644
--- a/code/modules/modular_computers/hardware/nano_printer.dm
+++ b/code/modules/modular_computers/hardware/nano_printer.dm
@@ -9,7 +9,7 @@
var/obj/item/weapon/paper/P = null // Currently stored paper for scanning.
-/obj/item/weapon/computer_hardware/nano_printer/proc/print_text(var/text_to_print)
+/obj/item/weapon/computer_hardware/nano_printer/proc/print_text(var/text_to_print, var/paper_title = null)
if(!stored_paper)
return 0
@@ -21,6 +21,8 @@
P = new/obj/item/weapon/paper(get_turf(holder2))
P.info = text_to_print
+ if(paper_title)
+ P.name = paper_title
P.update_icon()
stored_paper--
P = null
diff --git a/nano/templates/identification_computer.tmpl b/nano/templates/identification_computer.tmpl
index e783d9e3af4..e08684cf3f0 100644
--- a/nano/templates/identification_computer.tmpl
+++ b/nano/templates/identification_computer.tmpl
@@ -1,236 +1,180 @@
-{{if data.printing}}
- The computer is currently busy.
-
-
Printing...
-
-
- Thank you for your patience!
-
+{{if data.have_id_slot}}{{:helper.link('Access Modification', 'home', {'action' : 'switchm', 'target' : 'mod'}, data.mmode ? 'disabled' : null)}}{{/if}}
+{{:helper.link('Crew Manifest', 'folder-open', {'action' : 'switchm', 'target' : 'manifest'}, !data.mmode ? 'disabled' : null)}}
+{{if data.have_printer}}{{:helper.link('Print', 'print', {'action' : 'print'}, (!data.mmode || data.has_id) ? null : 'disabled')}}{{/if}}
+
+{{if !data.mmode}}
+
+
Crew Manifest
+
+
+ {{:data.manifest}}
+
{{else}}
- {{:helper.link('Access Modification', 'home', {'choice' : 'mode', 'mode_target' : 0}, !data.mode ? 'disabled' : null)}}
- {{:helper.link('Crew Manifest', 'folder-open', {'choice' : 'mode', 'mode_target' : 1}, data.mode ? 'disabled' : null)}}
- {{:helper.link('Print', 'print', {'choice' : 'print'}, (data.mode || data.has_modify) ? null : 'disabled')}}
+
+
Access Modification
+
- {{if data.mode}}
-
-
Crew Manifest
-
-
- {{:data.manifest}}
-
- {{else}}
-
-
Access Modification
-
+{{if !data.has_id}}
+ Please insert the ID into the terminal to proceed.
+{{/if}}
- {{if !data.authenticated}}
- Please insert the IDs into the terminal to proceed.
- {{/if}}
+
+
+ Target Identity:
+
+
+ {{:helper.link(data.id_name, 'eject', {'action' : 'eject'})}}
+
+
+
-
-
- Target Identity:
-
-
- {{:helper.link(data.target_name, 'eject', {'choice' : 'modify'})}}
-
-
-
-
- Authorized Identity:
-
-
- {{:helper.link(data.scan_name, 'eject', {'choice' : 'scan'})}}
-
-
-
+{{if data.authenticated}}
+ {{if data.has_id}}
+
+
Details
+
- {{if data.authenticated}}
-
+
+
+ Registered Name:
+
+
+ {{:helper.link(data.id_owner, 'pencil', {'action' : 'edit', 'name' : 1})}}
+
+
- {{if data.has_modify}}
-
-
Details
-
+
+
+ Account Number:
+
+
+ {{:helper.link(data.id_account_number, 'pencil', {'action' : 'edit', 'account' : 1})}}
+
+
+
-
+
+
+ Terminations:
+
+
+ {{:helper.link('Terminate ' + data.id_owner, 'gear', {'action' : 'terminate'}, data.id_rank == "Terminated" ? 'disabled' : null, data.id_rank == "Terminated" ? 'disabled' : 'linkDanger')}}
+
+
-
+
+
Assignment
+
+ {{:helper.link(data.assignments ? "Hide assignments" : "Show assignments", 'gear', {'action' : 'togglea'})}}
+
+
+
+
+
+
+ {{if data.assignments}}
+
+
+
+ | Command |
+
+
+ | Special |
+
+ {{:helper.link("Captain", '', {'action' : 'assign', 'assign_target' : 'Captain'}, data.id_rank == 'Captain' ? 'disabled' : null)}}
+ {{:helper.link("Custom", '', {'action' : 'assign', 'assign_target' : 'Custom'})}}
+ |
+
+
+ | Engineering |
+
+ {{for data.engineering_jobs}}
+ {{:helper.link(value.display_name, '', {'action' : 'assign', 'assign_target' : value.job}, data.id_rank == value.job ? 'disabled' : null)}}
+ {{/for}}
+ |
+
+
+ | Medical |
+
+ {{for data.medical_jobs}}
+ {{:helper.link(value.display_name, '', {'action' : 'assign', 'assign_target' : value.job}, data.id_rank == value.job ? 'disabled' : null)}}
+ {{/for}}
+ |
+
+
+ | Science |
+
+ {{for data.science_jobs}}
+ {{:helper.link(value.display_name, '', {'action' : 'assign', 'assign_target' : value.job}, data.id_rank == value.job ? 'disabled' : null)}}
+ {{/for}}
+ |
+
+
+ | Security |
+
+ {{for data.security_jobs}}
+ {{:helper.link(value.display_name, '', {'action' : 'assign', 'assign_target' : value.job}, data.id_rank == value.job ? 'disabled' : null)}}
+ {{/for}}
+ |
+
+
+ | Civilian |
+
+ {{for data.civilian_jobs}}
+ {{if index && index % 6 === 0}}
+ |
|
+ {{/if}}
+ {{:helper.link(value.display_name, '', {'action' : 'assign', 'assign_target' : value.job}, data.id_rank == value.job ? 'disabled' : null)}}
+ {{/for}}
+ |
+
+ {{if data.centcom_access}}
+
+ | CentCom |
+
+ {{for data.centcom_jobs}}
+ {{if index % 6 === 0}}
+ |
|
+ {{/if}}
-
-
- Terminations:
-
-
- {{:helper.link('Terminate ' + data.target_owner, 'gear', {'choice' : 'terminate'}, data.target_rank == "Terminated" ? 'disabled' : null, data.target_rank == "Terminated" ? 'disabled' : 'linkDanger')}}
-
-
+ {{:helper.link(value.display_name, '', {'action' : 'assign', 'assign_target' : value.job}, data.id_rank == value.job ? 'disabled' : null)}}
+ {{/for}}
+ |
+
+ {{/if}}
+
+
+ {{/if}}
+
-
-
Assignment
-
-
-
-
-
-
-
- | Command |
-
-
- | Special |
-
- {{:helper.link("Captain", '', {'choice' : 'assign', 'assign_target' : 'Captain'}, data.target_rank == 'Captain' ? 'disabled' : null)}}
- {{:helper.link("Custom", '', {'choice' : 'assign', 'assign_target' : 'Custom'})}}
- |
-
-
- | Engineering |
-
- {{for data.engineering_jobs}}
- {{:helper.link(value.display_name, '', {'choice' : 'assign', 'assign_target' : value.job}, data.target_rank == value.job ? 'disabled' : null)}}
- {{/for}}
- |
-
-
- | Medical |
-
- {{for data.medical_jobs}}
- {{:helper.link(value.display_name, '', {'choice' : 'assign', 'assign_target' : value.job}, data.target_rank == value.job ? 'disabled' : null)}}
- {{/for}}
- |
-
-
- | Science |
-
- {{for data.science_jobs}}
- {{:helper.link(value.display_name, '', {'choice' : 'assign', 'assign_target' : value.job}, data.target_rank == value.job ? 'disabled' : null)}}
- {{/for}}
- |
-
-
- | Security |
-
- {{for data.security_jobs}}
- {{:helper.link(value.display_name, '', {'choice' : 'assign', 'assign_target' : value.job}, data.target_rank == value.job ? 'disabled' : null)}}
- {{/for}}
- |
-
-
- | Civilian |
-
- {{for data.civilian_jobs}}
- {{if index && index % 6 === 0}}
- |
|
- {{/if}}
- {{:helper.link(value.display_name, '', {'choice' : 'assign', 'assign_target' : value.job}, data.target_rank == value.job ? 'disabled' : null)}}
- {{/for}}
- |
-
- {{if data.centcom_access}}
-
- | CentCom |
-
- {{for data.centcom_jobs}}
- {{if index % 6 === 0}}
- |
|
- {{/if}}
-
- {{:helper.link(value.display_name, '', {'choice' : 'assign', 'assign_target' : value.job}, data.target_rank == value.job ? 'disabled' : null)}}
- {{/for}}
- |
-
- {{/if}}
-
-
-
-
- {{if data.centcom_access}}
-
-
Central Command
-
-
- {{for data.all_centcom_access}}
-
- {{:helper.link(value.desc, '', {'choice' : 'access', 'access_target' : value.ref, 'allowed' : value.allowed}, null, value.allowed ? 'selected' : null)}}
-
- {{/for}}
-
- {{else}}
-
-
{{:data.station_name}}
-
-
- {{for data.regions}}
-
-
{{:value.name}}
- {{for value.accesses :accessValue:accessKey}}
-
- {{:helper.link(accessValue.desc, '', {'choice' : 'access', 'access_target' : accessValue.ref, 'allowed' : accessValue.allowed}, null, accessValue.allowed ? 'selected' : null)}}
-
- {{/for}}
-
- {{/for}}
-
- {{/if}}
- {{/if}}
- {{/if}}
+ {{if data.centcom_access}}
+
+
Central Command
+
+
+ {{for data.all_centcom_access}}
+
+ {{:helper.link(value.desc, '', {'action' : 'access', 'access_target' : value.ref, 'allowed' : value.allowed}, null, value.allowed ? 'selected' : null)}}
+
+ {{/for}}
+
+ {{else}}
+
+
{{:data.station_name}}
+
+
+ {{for data.regions}}
+
+
{{:value.name}}
+ {{for value.accesses :accessValue:accessKey}}
+
+ {{:helper.link(accessValue.desc, '', {'action' : 'access', 'access_target' : accessValue.ref, 'allowed' : accessValue.allowed}, null, accessValue.allowed ? 'selected' : null)}}
+
+ {{/for}}
+
+ {{/for}}
+
+ {{/if}}
{{/if}}
{{/if}}
+{{/if}}
\ No newline at end of file