diff --git a/baystation12.dme b/baystation12.dme
index 4851c962cb..cc513575bf 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -804,10 +804,11 @@
#include "code\modules\customitems\item_defines.dm"
#include "code\modules\customitems\item_spawning.dm"
#include "code\modules\destilery\main.dm"
-#include "code\modules\detectivework\detective_work.dm"
#include "code\modules\detectivework\evidence.dm"
#include "code\modules\detectivework\footprints_and_rag.dm"
+#include "code\modules\detectivework\forensics.dm"
#include "code\modules\detectivework\scanner.dm"
+#include "code\modules\detectivework\scanning_console.dm"
#include "code\modules\economy\Accounts.dm"
#include "code\modules\economy\Accounts_DB.dm"
#include "code\modules\economy\ATM.dm"
diff --git a/code/modules/detectivework/detective_work.dm b/code/modules/detectivework/detective_work.dm
deleted file mode 100644
index 6650046512..0000000000
--- a/code/modules/detectivework/detective_work.dm
+++ /dev/null
@@ -1,629 +0,0 @@
-//CONTAINS: Suit fibers and Detective's Scanning Computer
-
-atom/var/list/suit_fibers
-
-atom/proc/add_fibers(mob/living/carbon/human/M)
- if(M.gloves && istype(M.gloves,/obj/item/clothing/))
- var/obj/item/clothing/gloves/G = M.gloves
- if(G.transfer_blood) //bloodied gloves transfer blood to touched objects
- if(add_blood(G.bloody_hands_mob)) //only reduces the bloodiness of our gloves if the item wasn't already bloody
- G.transfer_blood--
- else if(M.bloody_hands)
- if(add_blood(M.bloody_hands_mob))
- M.bloody_hands--
- if(!suit_fibers) suit_fibers = list()
- var/fibertext
- var/item_multiplier = istype(src,/obj/item)?1.2:1
- if(M.wear_suit)
- fibertext = "Material from \a [M.wear_suit]."
- if(prob(10*item_multiplier) && !(fibertext in suit_fibers))
- //world.log << "Added fibertext: [fibertext]"
- suit_fibers += fibertext
- if(M.wear_suit.body_parts_covered & ~FULL_BODY && (M.wear_suit.body_parts_covered & ~UPPER_TORSO || M.wear_suit.body_parts_covered & ~LOWER_TORSO || M.wear_suit.body_parts_covered & ~ARMS || M.wear_suit.body_parts_covered & ~LEGS))
- if(M.w_uniform)
- fibertext = "Fibers from \a [M.w_uniform]."
- if(prob(12*item_multiplier) && !(fibertext in suit_fibers)) //Wearing a suit means less of the uniform exposed.
- //world.log << "Added fibertext: [fibertext]"
- suit_fibers += fibertext
- if(M.wear_suit.body_parts_covered & ~HANDS)
- if(M.gloves)
- fibertext = "Material from a pair of [M.gloves.name]."
- if(prob(20*item_multiplier) && !(fibertext in suit_fibers))
- //world.log << "Added fibertext: [fibertext]"
- suit_fibers += fibertext
- else if(M.w_uniform)
- fibertext = "Fibers from \a [M.w_uniform]."
- if(prob(15*item_multiplier) && !(fibertext in suit_fibers))
- // "Added fibertext: [fibertext]"
- suit_fibers += fibertext
- if(M.gloves)
- fibertext = "Material from a pair of [M.gloves.name]."
- if(prob(20*item_multiplier) && !(fibertext in suit_fibers))
- //world.log << "Added fibertext: [fibertext]"
- suit_fibers += "Material from a pair of [M.gloves.name]."
- else if(M.gloves)
- fibertext = "Material from a pair of [M.gloves.name]."
- if(prob(20*item_multiplier) && !(fibertext in suit_fibers))
- //world.log << "Added fibertext: [fibertext]"
- suit_fibers += "Material from a pair of [M.gloves.name]."
- if(!suit_fibers.len) del suit_fibers
-
-var/const/FINGERPRINT_COMPLETE = 6 //This is the output of the stringpercent(print) proc, and means about 80% of
- //the print must be there for it to be complete. (Prints are 32 digits)
-
-obj/machinery/computer/forensic_scanning
- name = "\improper High-Res Forensic Scanning Computer"
- icon_state = "forensic"
- var/obj/item/scanning
- var/temp = ""
- var/canclear = 1
- var/authenticated = 0
-
-//Here's the structure for files: each entry is a list, and entry one in that list is the string of their
-//full and scrambled fingerprint. This acts as the method to arrange evidence. Each subsequent entry is list
-//in the form (from entries):
-// 1: Object
-// 2: All prints on the object
-// 3: All fibers on the object
-// 4: All blood on the object
-//This is then used to show what objects were used to "find" the full print, as well as the fibers on it.
- var/list/files
-//This holds objects (1) without prints, and their fibers(2) and blood(3).
- var/list/misc
- var/obj/item/weapon/f_card/card
-
- var/scan_data = ""
- var/scan_name = ""
- var/scan_process = 0
-
- req_access = list(access_forensics_lockers)
-
-
- New()
- ..()
- new /obj/item/weapon/book/manual/detective(get_turf(src))
- return
-
-
- attack_ai(mob/user)
- return attack_hand(user)
-
-
- attack_hand(mob/user)
- if(..())
- return
- user.set_machine(src)
- var/dat = ""
- var/isai = 0
- if(istype(usr,/mob/living/silicon))
- isai = 1
- if(temp)
- dat += "[temp]
"
- if(canclear) dat += "{Clear Screen}"
- else
- if(!authenticated)
- dat += "{Log In}"
- else
- dat += "{Log Out}
"
- if(scanning)
- if(scan_process)
- dat += {"Scan Object: {[scanning.name]}
- {Cancel Scan} {Print}
"}
- else
- if(isai) dat += "Scan Object: {[scanning.name]}
"
- else dat += "Scan Object: {[scanning.name]}
"
- dat += "{Scan} {Print}
"
- else
- if(isai) dat += "{No Object Inserted}
"
- else dat += "{No Object Inserted}
"
- dat += "{Scan} {Print}
"
- dat += {"{Access Database}
"
- [scan_data]"}
- if(scan_data && !scan_process)
- dat += "
{Erase Data}"
- user << browse(dat,"window=scanner")
- onclose(user,"scanner")
-
-
- Topic(href,href_list)
- if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
- switch(href_list["operation"])
- if("login")
- var/mob/M = usr
- if(istype(M,/mob/living/silicon))
- authenticated = 1
- updateDialog()
- return
- if (allowed(M))
- authenticated = 1
- if("logout")
- authenticated = 0
- if("clear")
- if(canclear)
- temp = null
- if("eject")
- if(scanning)
- scanning.loc = loc
- scanning = null
- else
- temp = "Eject Failed: No Object"
- if("insert")
- var/mob/M = usr
- var/obj/item/I = M.get_active_hand()
- if(I && istype(I))
- if(istype(I, /obj/item/weapon/evidencebag))
- scanning = I.contents[1]
- scanning.loc = src
- I.overlays -= scanning
- I.icon_state = "evidenceobj"
- else
- scanning = I
- M.drop_item()
- I.loc = src
- else
- usr << "Invalid Object Rejected."
- if("card") //Processing a fingerprint card.
- var/mob/M = usr
- var/obj/item/I = M.get_active_hand()
- if(!(I && istype(I,/obj/item/weapon/f_card)))
- I = card
- if(I && istype(I,/obj/item/weapon/f_card))
- card = I
- if(!card.fingerprints)
- card.fingerprints = list()
- if(card.amount > 1 || !card.fingerprints.len)
- usr << "\red ERROR: No prints/too many cards."
- if(card.loc == src)
- card.loc = src.loc
- card = null
- return
- M.drop_item()
- I.loc = src
- process_card()
- else
- usr << "\red Invalid Object Rejected."
- if("database") //Viewing all records in each database
- canclear = 1
- if(href_list["delete_record"])
- delete_dossier(href_list["delete_record"])
- if(href_list["delete_aux"])
- delete_record(href_list["delete_aux"])
- if((!misc || !misc.len) && (!files || !files.len))
- temp = "Database is empty."
- else
- if(files && files.len)
- temp = "Criminal Evidence Database
"
- temp += "Consolidated data points:
"
- for(var/print in files)
- var/list/file = files[print]
- temp += "{[file[2]]}
"
- temp += "
{Insert Finger Print Card (To complete a Dossier)}
"
- else
- temp = ""
- if(misc && misc.len)
- temp += {"Auxiliary Evidence Database
- This is where anything without fingerprints goes.
"}
- for(var/atom in misc)
- var/list/data_entry = misc[atom]
- temp += "{[data_entry[3]]}
"
- if("record") //Viewing a record from the "files" database.
- canclear = 0
- if(files)
- var/list/dossier = files[href_list["identifier"]]
- if(href_list["ren"])
- var/new_title = copytext(sanitize(input("Rename to what?", "Dossier Editing", "Dossier [files.Find(href_list["identifier"])]") as null|text),1,MAX_MESSAGE_LEN)
- if(new_title)
- dossier[2] = new_title
- else
- usr << "Illegal or blank name."
- temp = {"Criminal Evidence Database
- Consolidated data points: [dossier[2]]
"}
- var/print_string = "Fingerprints: Print not complete!
"
- if(stringpercent(dossier[1]) <= FINGERPRINT_COMPLETE)
- print_string = "Fingerprints: (80% or higher completion reached)
[dossier[1]]
"
- temp += print_string
- for(var/object in dossier)
- if(object == dossier[1] || object == dossier[2])
- continue
- temp += "
"
- var/list/outputs = dossier[object]
- var/list/prints = outputs[1]
- temp += "Object: [outputs[4]]
"
- temp += " Fingerprints:
"
- temp += " [prints.len] Unique fingerprints found.
"
- var/complete_prints = 0
- for(var/print in prints)
- if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
- complete_prints++
- temp += " [prints[print]]
"
- if(complete_prints)
- temp += " And [prints.len - complete_prints] unknown unique prints.
"
- else
- temp += " No prints of sufficient completeness.
"
- var/list/fibers = outputs[2]
- if(fibers && fibers.len)
- temp += " Fibers:
"
- for(var/j = 1, j <= fibers.len, j++)
- temp += " [fibers[j]]
"
- var/list/blood = outputs[3]
- if(blood && blood.len)
- temp += " Blood:
"
- for(var/named in blood)
- temp += " Type: [blood[named]], DNA: [named]
"
- temp += "
{Rename this Dossier}"
- temp += "
{Delete this Dossier}"
- temp += "
{Print}"
- else
- temp = "ERROR. Database not found!
"
- temp += "
{Return}"
- if("databaseprint") //Printing from the "files" database.
- if(files)
- var/obj/item/weapon/paper/P = new(loc)
- var/list/dossier = files[href_list["identifier"]]
- P.name = "\improper Database File ([dossier[2]])"
- P.icon_state = "paper_words"
- P.info = "Criminal Evidence Database
"
- P.info += "Consolidated data points: [dossier[2]]
"
- var/print_string = "Fingerprints: Print not complete!
"
- if(stringpercent(dossier[1]) <= FINGERPRINT_COMPLETE)
- print_string = "Fingerprints: (80% or higher completion reached)
[dossier[1]]
"
- P.info += print_string
- for(var/object in dossier)
- if(object == dossier[1] || object == dossier[2])
- continue
- P.info += "
"
- var/list/outputs = dossier[object]
- var/list/prints = outputs[1]
- P.info += "Object: [outputs[4]]
"
- P.info += " Fingerprints:
"
- P.info += " [prints.len] Unique fingerprints found.
"
- var/complete_prints = 0
- for(var/print in prints)
- if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
- complete_prints++
- P.info += " [prints[print]]
"
- if(complete_prints)
- P.info += " And [prints.len - complete_prints] unknown unique prints.
"
- else
- P.info += " No prints of sufficient completeness.
"
- var/list/fibers = outputs[2]
- if(fibers && fibers.len)
- P.info += " Fibers:
"
- for(var/j = 1, j <= fibers.len, j++)
- P.info += " [fibers[j]]
"
- var/list/blood = outputs[3]
- if(blood && blood.len)
- P.info += " Blood:
"
- for(var/named in blood)
- P.info += " Type: [blood[named]], DNA: [named]
"
- else
- usr << "ERROR. Database not found!
"
- if("auxiliary") //Viewing a record from the "misc" database.
- canclear = 0
- if(misc)
- temp = "Auxiliary Evidence Database
"
- var/list/outputs = misc[href_list["identifier"]]
- temp += "Consolidated data points: [outputs[3]]
"
- var/list/prints = outputs[4]
- if(prints)
- temp += " Fingerprints:
"
- temp += " [prints.len] Unique fingerprints found.
"
- var/complete_prints = 0
- for(var/print in prints)
- if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
- complete_prints++
- temp += " [prints[print]]
"
- if(complete_prints)
- temp += " And [prints.len - complete_prints] unknown unique prints.
"
- else
- temp += " No prints of sufficient completeness.
"
- var/list/fibers = outputs[1]
- if(fibers && fibers.len)
- temp += " Fibers:
"
- for(var/fiber in fibers)
- temp += " [fiber]
"
- var/list/blood = outputs[2]
- if(blood && blood.len)
- temp += " Blood:
"
- for(var/named in blood)
- temp += " Type: [blood[named]], DNA: [named]
"
- temp += "
{Delete This Record}"
- temp += "
{Print}"
- else
- temp = "ERROR. Database not found!
"
- temp += "
{Return}"
- if("auxiliaryprint") //Printing from the "misc" database.
- if(misc)
- var/obj/item/weapon/paper/P = new(loc)
- var/list/outputs = misc[href_list["identifier"]]
- P.name = "\improper Auxiliary Database File ([outputs[3]])"
- P.icon_state = "paper_words"
- P.info = "Auxiliary Evidence Database
"
- P.info += "Consolidated data points: [outputs[3]]
"
- var/list/prints = outputs[4]
- if(prints)
- P.info += " Fingerprints:
"
- P.info += " [prints.len] Unique fingerprints found.
"
- var/complete_prints = 0
- for(var/print in prints)
- if(stringpercent(prints[print]) <= FINGERPRINT_COMPLETE)
- complete_prints++
- P.info += " [prints[print]]
"
- if(complete_prints)
- P.info += " And [prints.len - complete_prints] unknown unique prints.
"
- else
- P.info += " No prints of sufficient completeness.
"
- var/list/fibers = outputs[1]
- if(fibers && fibers.len)
- P.info += " Fibers:
"
- for(var/fiber in fibers)
- P.info += " [fiber]
"
- var/list/blood = outputs[2]
- if(blood && blood.len)
- P.info += " Blood:
"
- for(var/named in blood)
- P.info += " Type: [blood[named]], DNA: [named]
"
- else
- usr << "ERROR. Database not found!
"
- if("scan")
- if(istype(scanning,/obj/item/weapon/f_card))
- card = scanning
- scanning = initial(scanning)
- process_card()
- else if(scanning)
- scan_process = 3
- scan_data = "Scanning [scanning]: 25% complete"
- updateDialog()
- sleep(50)
- if(!scan_process)
- scan_data = null
- updateDialog()
- return
- scan_data = "Scanning [scanning]: 50% complete"
- updateDialog()
- scan_process = 2
- sleep(50)
- if(!scan_process)
- scan_data = null
- updateDialog()
- return
- scan_data = "Scanning [scanning]: 75% complete"
- updateDialog()
- scan_process = 1
- sleep(50)
- if(!scan_process)
- scan_data = null
- updateDialog()
- return
- if(scanning)
- scan_process = 0
- scan_name = scanning.name
- scan_data = "[scanning]
"
- if (scanning.blood_DNA)
- scan_data += "Blood Found:
"
- for(var/blood in scanning.blood_DNA)
- scan_data += "Blood type: [scanning.blood_DNA[blood]]\nDNA: [blood]
"
- else
- scan_data += "No Blood Found
"
- if(!scanning.fingerprints)
- scan_data += "No Fingerprints Found
"
- else
- scan_data += "Isolated [scanning.fingerprints.len] Fingerprints. Loaded into database.
"
- add_data(scanning)
-
- if(!scanning.suit_fibers)
- scan_data += "No Fibers/Materials Located
"
- else
- scan_data += "Fibers/Materials Found:
"
- for(var/data in scanning.suit_fibers)
- scan_data += "- [data]
"
- if(istype(scanning,/obj/item/device/detective_scanner) || (istype(scanning, /obj/item/device/pda) && scanning:cartridge && scanning:cartridge.access_security))
- scan_data += "
Data transfered from \the [scanning] to Database.
"
- add_data_scanner(scanning)
- else if(!scanning.fingerprints)
- scan_data += "
Add to Database?
"
- else
- temp = "Scan Failed: No Object"
-
-
- if("print") //Printing scan data
- if(scan_data)
- temp = "Scan Data Printed."
- var/obj/item/weapon/paper/P = new(loc)
- P.name = "\improper Scan Data ([scan_name])"
- P.info = "[scan_data]"
- P.icon_state = "paper_words"
- else
- temp = "Print Failed: No Data"
- if("erase")
- scan_data = ""
- if("cancel")
- scan_process = 0
- if("add") //Adding an object (Manually) to the database.
- if(scanning)
- add_data(scanning)
- else
- temp = "Data Transfer Failed: No Object."
- if("rename")
- if(!files || !files[href_list["identifier"]])
- temp = "ERROR: Record/Database not found!"
- else
- var/new_title = copytext(sanitize(input("Rename to what?", "Dossier Editing", "Dossier [files.Find(href_list["identifier"])]") as null|text),1,MAX_MESSAGE_LEN)
- if(new_title)
- var/list/file = files[href_list["identifier"]]
- file[2] = new_title
- updateUsrDialog()
-
- ex_act()
- return
-
-
- proc/add_data_scanner(var/obj/item/device/W)
- if(istype(W, /obj/item/device/detective_scanner))
- var/obj/item/device/detective_scanner/D = W
- if(D.stored)
- for(var/atom in D.stored)
- var/list/data = D.stored[atom]
- add_data_master(atom,data[1],data[2],data[3],data[4])
- D.stored = list()
- else if(istype(W, /obj/item/device/pda) && W:cartridge && W:cartridge.access_security)
- if(W:cartridge.stored_data)
- for(var/atom in W:cartridge.stored_data)
- var/list/data = W:cartridge.stored_data[atom]
- add_data_master(atom,data[1],data[2],data[3],data[4])
- W:cartridge.stored_data = list()
- return
-
- proc/add_data(var/atom/scanned_atom)
- return add_data_master("\ref [scanned_atom]", scanned_atom.fingerprints,\
- scanned_atom.suit_fibers, scanned_atom.blood_DNA, "[scanned_atom.name] (Direct Scan)")
-
-
-
-/********************************
-*****DO NOT DIRECTLY CALL ME*****
-********************************/
- proc/add_data_master(var/atom_reference, var/list/atom_fingerprints, var/list/atom_suit_fibers, var/list/atom_blood_DNA, var/atom_name)
-//What follows is massive. It cross references all stored data in the scanner with the other stored data,
-//and what is already in the computer. Not sure how bad the lag may/may not be.
-
- if(!misc)
- misc = list()
- var/list/data_entry = misc[atom_reference]
- if(data_entry)
- var/list/fibers = data_entry[1]
- if(!fibers)
- fibers = list()
- if(atom_suit_fibers)
- for(var/fiber in atom_suit_fibers) //Fibers~~~
- if(!fibers.Find(fiber)) //It isn't! Add!
- fibers += fiber
- var/list/blood = data_entry[2]
- if(!blood)
- blood = list()
- if(atom_blood_DNA)
- for(var/main_blood in atom_blood_DNA)
- if(!blood[main_blood])
- blood[main_blood] = atom_blood_DNA[blood]
- var/list/prints = data_entry[4]
- if(!prints && atom_fingerprints)
- prints = list()
- if(atom_fingerprints)
- for(var/print in atom_fingerprints)
- if(!prints[print])
- prints[print] = atom_fingerprints[print]
- else
- var/list/templist[4]
- templist[1] = atom_suit_fibers ? atom_suit_fibers.Copy() : null
- templist[2] = atom_blood_DNA ? atom_blood_DNA.Copy() : null
- templist[3] = atom_name
- templist[4] = atom_fingerprints ? atom_fingerprints.Copy() : null
- misc[atom_reference] = templist //Store it!
- //Has prints.
- if(atom_fingerprints)
- if(!files)
- files = list()
- for(var/main_print in atom_fingerprints)
- data_entry = files[main_print]
- if(data_entry)//The print is already in here!
- var/list/internal_atom = data_entry[atom_reference] //Lets see if we can find the current object
- if(internal_atom)
- //We must be on a roll! Just update what needs to be updated.
- var/list/internal_prints = internal_atom[1]
- for(var/print in atom_fingerprints) //Sorry for the double loop! D:
- var/associated_print = internal_prints[print]
- var/reference_print = atom_fingerprints[print]
- if(associated_print && associated_print != reference_print) //It does not match
- internal_prints[print] = stringmerge(associated_print, reference_print)
- else if(!associated_print)
- internal_prints[print] = reference_print
- //If the main print was updated, lets update the master as well.
- if(print == main_print && (!associated_print || (associated_print && associated_print != reference_print)))
- update_fingerprints(main_print, internal_prints[print])
- //Fibers.
- var/list/fibers = internal_atom[2]
- if(!fibers)
- fibers = list()
- if(atom_suit_fibers)
- for(var/fiber in atom_suit_fibers) //Fibers~~~
- if(!fibers.Find(fiber)) //It isn't! Add!
- fibers += fiber
- //Blood.
- var/list/blood = internal_atom[3]
- if(!blood)
- blood = list()
- if(atom_blood_DNA)
- for(var/main_blood in atom_blood_DNA)
- if(!blood[main_blood])
- blood[main_blood] = atom_blood_DNA[blood]
-
- continue
- //It's not in there! We gotta add it.
- update_fingerprints(main_print, atom_fingerprints[main_print])
- var/list/data_point[4]
- data_point[1] = atom_fingerprints ? atom_fingerprints.Copy() : null
- data_point[2] = atom_suit_fibers ? atom_suit_fibers.Copy() : null
- data_point[3] = atom_blood_DNA ? atom_blood_DNA.Copy() : null
- data_point[4] = atom_name
- data_entry[atom_reference] = data_point
- continue
- //No print at all! New data entry, go!
- var/list/data_point[4]
- data_point[1] = atom_fingerprints ? atom_fingerprints.Copy() : null
- data_point[2] = atom_suit_fibers ? atom_suit_fibers.Copy() : null
- data_point[3] = atom_blood_DNA ? atom_blood_DNA.Copy() : null
- data_point[4] = atom_name
- var/list/new_file[2]
- new_file[1] = atom_fingerprints[main_print]
- new_file[2] = "Dossier [files.len + 1]"
- new_file[atom_reference] = data_point
- files[main_print] = new_file
- return 1
-/********************************
-***END DO NOT DIRECTLY CALL ME***
-********************************/
-
- proc/update_fingerprints(var/ref_print, var/new_print)
- var/list/master = files[ref_print]
- if(master)
- master[1] = stringmerge(master[1],new_print)
- else
- CRASH("Fucking hell. Something went wrong, and it tried to update a null print or something. Tell SkyMarshal (and give him this call stack)")
- return
-
- proc/process_card() //Same as above, but for fingerprint cards
- if(card.fingerprints && !(card.amount > 1) && islist(card.fingerprints) && files && files.len)
- usr << "You insert the card, and it is destroyed by the machinery in the process of comparing prints."
- var/found = 0
- for(var/master_print in card.fingerprints)
- var/list/data_entry = files[master_print]
- if(data_entry)
- found = 1
- data_entry[1] = master_print
- if(found)
- usr << "The machinery finds it can complete a match."
- else
- usr << "No match found."
- del(card)
- else
- usr << "\red ERROR: No prints/too many cards."
- if(card.loc == src)
- card.loc = src.loc
- card = null
- return
- return
-
- proc/delete_record(var/atom_ref) //Deletes an entry in the misc database at the given location
- if(misc && misc.len)
- misc.Remove(atom_ref)
- return
-
- proc/delete_dossier(var/print) //Deletes a Dossier at a given location.
- if(files && files.len)
- files.Remove(print)
- return
-
- detective
- icon_state = "old"
- name = "PowerScan Mk.I"
\ No newline at end of file
diff --git a/code/modules/detectivework/forensics.dm b/code/modules/detectivework/forensics.dm
new file mode 100644
index 0000000000..d04fe83f46
--- /dev/null
+++ b/code/modules/detectivework/forensics.dm
@@ -0,0 +1,73 @@
+//This is the output of the stringpercent(print) proc, and means about 80% of
+//the print must be there for it to be complete. (Prints are 32 digits)
+var/const/FINGERPRINT_COMPLETE = 6
+proc/is_complete_print(var/print)
+ return stringpercent(print) <= FINGERPRINT_COMPLETE
+
+atom/var/list/suit_fibers
+
+atom/proc/add_fibers(mob/living/carbon/human/M)
+ if(M.gloves && istype(M.gloves,/obj/item/clothing/gloves))
+ var/obj/item/clothing/gloves/G = M.gloves
+ if(G.transfer_blood) //bloodied gloves transfer blood to touched objects
+ if(add_blood(G.bloody_hands_mob)) //only reduces the bloodiness of our gloves if the item wasn't already bloody
+ G.transfer_blood--
+ else if(M.bloody_hands)
+ if(add_blood(M.bloody_hands_mob))
+ M.bloody_hands--
+
+ if(!suit_fibers) suit_fibers = list()
+ var/fibertext
+ var/item_multiplier = istype(src,/obj/item)?1.2:1
+ var/suit_coverage = 0
+ if(M.wear_suit)
+ fibertext = "Material from \a [M.wear_suit]."
+ if(prob(10*item_multiplier) && !(fibertext in suit_fibers))
+ suit_fibers += fibertext
+ suit_coverage = M.wear_suit.body_parts_covered
+
+ if(M.w_uniform && (M.w_uniform.body_parts_covered & ~suit_coverage))
+ fibertext = "Fibers from \a [M.w_uniform]."
+ if(prob(15*item_multiplier) && !(fibertext in suit_fibers))
+ suit_fibers += fibertext
+
+ if(M.gloves && (M.w_uniform.body_parts_covered & ~suit_coverage))
+ fibertext = "Material from a pair of [M.gloves.name]."
+ if(prob(20*item_multiplier) && !(fibertext in suit_fibers))
+ suit_fibers += "Material from a pair of [M.gloves.name]."
+
+/datum/data/record/forensic
+ name = "forensic data"
+ var/uid
+
+/datum/data/record/forensic/New(var/atom/A)
+ uid = "\ref [A]"
+ fields["name"] = sanitize(A.name)
+ fields["area"] = sanitize("[get_area(A)]")
+ fields["fprints"] = A.fingerprints ? A.fingerprints.Copy() : list()
+ fields["fibers"] = A.suit_fibers ? A.suit_fibers.Copy() : list()
+ fields["blood"] = A.blood_DNA ? A.blood_DNA.Copy() : list()
+ fields["time"] = world.time
+
+/datum/data/record/forensic/proc/merge(var/datum/data/record/other)
+ var/list/prints = fields["fprints"]
+ var/list/o_prints = other.fields["fprints"]
+ for(var/print in o_prints)
+ if(!prints[print])
+ prints[print] = o_prints[print]
+ else
+ prints[print] = stringmerge(prints[print], o_prints[print])
+ fields["fprints"] = prints
+
+ var/list/fibers = fields["fibers"]
+ var/list/o_fibers = other.fields["fibers"]
+ fibers |= o_fibers
+ fields["fibers"] = fibers
+
+ var/list/blood = other.fields["blood"]
+ var/list/o_blood = other.fields["blood"]
+ blood |= o_blood
+ fields["blood"] = blood
+
+ fields["area"] = other.fields["area"]
+ fields["time"] = other.fields["time"]
\ No newline at end of file
diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm
index 8ab4da0414..3916c799c8 100644
--- a/code/modules/detectivework/scanner.dm
+++ b/code/modules/detectivework/scanner.dm
@@ -1,181 +1,107 @@
-//CONTAINS: Detective's Scanner
-
-
/obj/item/device/detective_scanner
- name = "Scanner"
+ name = "forensic scanner"
desc = "Used to scan objects for DNA and fingerprints."
icon_state = "forensic1"
- var/amount = 20.0
var/list/stored = list()
w_class = 3.0
item_state = "electronic"
flags = FPRINT | TABLEPASS | CONDUCT | NOBLUDGEON
slot_flags = SLOT_BELT
- attackby(obj/item/weapon/f_card/W as obj, mob/user as mob)
- ..()
- if (istype(W, /obj/item/weapon/f_card))
- if (W.fingerprints)
- return
- if (src.amount == 20)
- return
- if (W.amount + src.amount > 20)
- src.amount = 20
- W.amount = W.amount + src.amount - 20
- else
- src.amount += W.amount
- //W = null
- del(W)
- add_fingerprint(user)
- if (W)
- W.add_fingerprint(user)
- return
-
- attack(mob/living/carbon/human/M as mob, mob/user as mob)
- if (!ishuman(M))
- user << "\red [M] is not human and cannot have the fingerprints."
- flick("forensic0",src)
- return 0
- if (( !( istype(M.dna, /datum/dna) ) || M.gloves) )
- user << "\blue No fingerprints found on [M]"
- flick("forensic0",src)
- return 0
- else
- if (src.amount < 1)
- user << text("\blue Fingerprints scanned on [M]. Need more cards to print.")
- else
- src.amount--
- var/obj/item/weapon/f_card/F = new /obj/item/weapon/f_card( user.loc )
- F.amount = 1
- F.add_fingerprint(M)
- F.icon_state = "fingerprint1"
- F.name = text("FPrintC- '[M.name]'")
-
- user << "\blue Done printing."
- user << "\blue [M]'s Fingerprints: [md5(M.dna.uni_identity)]"
- if ( !M.blood_DNA || !M.blood_DNA.len )
- user << "\blue No blood found on [M]"
- if(M.blood_DNA)
- del(M.blood_DNA)
- else
- user << "\blue Blood found on [M]. Analysing..."
- spawn(15)
- for(var/blood in M.blood_DNA)
- user << "\blue Blood type: [M.blood_DNA[blood]]\nDNA: [blood]"
- return
-
- afterattack(atom/A as obj|turf|area, mob/user as mob, proximity)
- if(!proximity) return
- if(loc != user)
- return
- if(istype(A,/obj/machinery/computer/forensic_scanning)) //breaks shit.
- return
-
- if(istype(A,/obj/item/weapon/f_card))
- user << "The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\""
- flick("forensic0",src)
- return
-
- add_fingerprint(user)
-
- //Special case for blood splatters, runes and gibs.
- if (istype(A, /obj/effect/decal/cleanable/blood) || istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable/blood/gibs))
- if(!isnull(A.blood_DNA))
- for(var/blood in A.blood_DNA)
- user << "\blue Blood type: [A.blood_DNA[blood]]\nDNA: [blood]"
- flick("forensic2",src)
- return
-
- //General
- if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA)
- user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\
- "\blue Unable to locate any fingerprints, materials, fibers, or blood on [A]!",\
- "You hear a faint hum of electrical equipment.")
- flick("forensic0",src)
- return 0
-
- if(add_data(A))
- user << "\blue Object already in internal memory. Consolidating data..."
- flick("forensic2",src)
- return
-
-
- //PRINTS
- if(!A.fingerprints || !A.fingerprints.len)
- if(A.fingerprints)
- del(A.fingerprints)
- else
- user << "\blue Isolated [A.fingerprints.len] fingerprints: Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
- var/list/complete_prints = list()
- for(var/i in A.fingerprints)
- var/print = A.fingerprints[i]
- if(stringpercent(print) <= FINGERPRINT_COMPLETE)
- complete_prints += print
- if(complete_prints.len < 1)
- user << "\blue No intact prints found"
- else
- user << "\blue Found [complete_prints.len] intact prints"
- for(var/i in complete_prints)
- user << "\blue [i]"
-
- //FIBERS
- if(A.suit_fibers)
- user << "\blue Fibers/Materials Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
- flick("forensic2",src)
-
- //Blood
- if (A.blood_DNA)
- user << "\blue Blood found on [A]. Analysing..."
- spawn(15)
- for(var/blood in A.blood_DNA)
- user << "Blood type: \red [A.blood_DNA[blood]] \t \black DNA: \red [blood]"
- if(prob(80) || !A.fingerprints)
- user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\
- "You finish scanning \the [A].",\
- "You hear a faint hum of electrical equipment.")
- flick("forensic2",src)
- return 0
- else
- user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]\n[user.gender == MALE ? "He" : "She"] seems to perk up slightly at the readout." ,\
- "The results of the scan pique your interest.",\
- "You hear a faint hum of electrical equipment, and someone making a thoughtful noise.")
- flick("forensic2",src)
- return 0
- return
-
- proc/add_data(atom/A as mob|obj|turf|area)
- //I love associative lists.
- var/list/data_entry = stored["\ref [A]"]
- if(islist(data_entry)) //Yay, it was already stored!
- //Merge the fingerprints.
- var/list/data_prints = data_entry[1]
- for(var/print in A.fingerprints)
- var/merged_print = data_prints[print]
- if(!merged_print)
- data_prints[print] = A.fingerprints[print]
- else
- data_prints[print] = stringmerge(data_prints[print],A.fingerprints[print])
-
- //Now the fibers
- var/list/fibers = data_entry[2]
- if(!fibers)
- fibers = list()
- if(A.suit_fibers && A.suit_fibers.len)
- for(var/j = 1, j <= A.suit_fibers.len, j++) //Fibers~~~
- if(!fibers.Find(A.suit_fibers[j])) //It isn't! Add!
- fibers += A.suit_fibers[j]
- var/list/blood = data_entry[3]
- if(!blood)
- blood = list()
- if(A.blood_DNA && A.blood_DNA.len)
- for(var/main_blood in A.blood_DNA)
- if(!blood[main_blood])
- blood[main_blood] = A.blood_DNA[blood]
- return 1
- var/list/sum_list[4] //Pack it back up!
- sum_list[1] = A.fingerprints ? A.fingerprints.Copy() : null
- sum_list[2] = A.suit_fibers ? A.suit_fibers.Copy() : null
- sum_list[3] = A.blood_DNA ? A.blood_DNA.Copy() : null
- sum_list[4] = "\The [A] in \the [get_area(A)]"
- stored["\ref [A]"] = sum_list
+/obj/item/device/detective_scanner/attack(mob/living/carbon/human/M as mob, mob/user as mob)
+ if (!ishuman(M))
+ user << "[M] is not human and cannot have the fingerprints."
+ flick("forensic0",src)
return 0
+ if (( !( istype(M.dna, /datum/dna) ) || M.gloves) )
+ user << "No fingerprints found on [M]"
+ flick("forensic0",src)
+ return 0
+ else
+ var/obj/item/weapon/f_card/F = new /obj/item/weapon/f_card( user.loc )
+ F.amount = 1
+ F.add_fingerprint(M)
+ F.icon_state = "fingerprint1"
+ F.name = text("FPrintC- '[M.name]'")
+ user << "Done printing."
+ user << "[M]'s Fingerprints: [md5(M.dna.uni_identity)]"
+ if ( M.blood_DNA && M.blood_DNA.len )
+ user << "Blood found on [M]. Analysing..."
+ spawn(15)
+ for(var/blood in M.blood_DNA)
+ user << "Blood type: [M.blood_DNA[blood]]\nDNA: [blood]"
+ return
+
+/obj/item/device/detective_scanner/afterattack(atom/A as obj|turf, mob/user, proximity)
+ if(!proximity) return
+ if(ismob(A))
+ return
+ if(istype(A,/obj/machinery/computer/forensic_scanning))
+ user.visible_message("[user] takes a cord out of [src] and hooks its end into [A]" ,\
+ "You download data from [src] to [A]")
+ var/obj/machinery/computer/forensic_scanning/F = A
+ F.sync_data(stored)
+ return
+
+ if(istype(A,/obj/item/weapon/f_card))
+ user << "The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\""
+ flick("forensic0",src)
+ return
+
+ add_fingerprint(user)
+
+ //General
+ if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA)
+ user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\
+ "Unable to locate any fingerprints, materials, fibers, or blood on [A]!",\
+ "You hear a faint hum of electrical equipment.")
+ flick("forensic0",src)
+ return 0
+
+ if(add_data(A))
+ user << "Object already in internal memory. Consolidating data..."
+ flick("forensic2",src)
+ return
+
+ //PRINTS
+ if(A.fingerprints && A.fingerprints.len)
+ user << "Isolated [A.fingerprints.len] fingerprints:"
+ user << "Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
+ var/list/complete_prints = list()
+ for(var/i in A.fingerprints)
+ var/print = A.fingerprints[i]
+ if(stringpercent(print) <= FINGERPRINT_COMPLETE)
+ complete_prints += print
+ if(complete_prints.len < 1)
+ user << "No intact prints found"
+ else
+ user << "Found [complete_prints.len] intact prints"
+ for(var/i in complete_prints)
+ user << " [i]"
+
+ //FIBERS
+ if(A.suit_fibers && A.suit_fibers.len)
+ user << "Fibers/Materials Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
+ flick("forensic2",src)
+
+ //Blood
+ if (A.blood_DNA && A.blood_DNA.len)
+ user << "Blood detected. Analysing..."
+ spawn(15)
+ for(var/blood in A.blood_DNA)
+ user << "Blood type: \red [A.blood_DNA[blood]] \t \black DNA: \red [blood]"
+
+ user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\
+ "You finish scanning \the [A].",\
+ "You hear a faint hum of electrical equipment.")
+ flick("forensic2",src)
+ return 0
+
+/obj/item/device/detective_scanner/proc/add_data(atom/A as mob|obj|turf|area)
+ var/datum/data/record/forensic/old = stored["\ref [A]"]
+ var/datum/data/record/forensic/fresh = new(A)
+
+ if(old)
+ fresh.merge(old)
+ . = 1
+ stored["\ref [A]"] = fresh
\ No newline at end of file
diff --git a/code/modules/detectivework/scanning_console.dm b/code/modules/detectivework/scanning_console.dm
new file mode 100644
index 0000000000..9e32c58390
--- /dev/null
+++ b/code/modules/detectivework/scanning_console.dm
@@ -0,0 +1,305 @@
+/obj/machinery/computer/forensic_scanning
+ name = "high-res forensic scanning computer"
+ icon_state = "forensic"
+
+ var/screen = "database"
+ var/authenticated = 0
+ req_access = list(access_forensics_lockers)
+ var/scan_progress = -1
+ var/obj/item/scanning
+ var/datum/data/record/forensic/current
+
+ var/list/filters = list()
+ var/list/current_list = list()
+
+ var/list/files = list()
+
+/obj/machinery/computer/forensic_scanning/proc/get_printable_data(var/datum/data/record/forensic/fresh)
+ . += "[fresh.fields["name"]]
"
+ . += "Scanned in [fresh.fields["area"]] at [worldtime2text(fresh.fields["time"])]
"
+ var/list/prints = fresh.fields["fprints"]
+ if(prints.len)
+ . += "Fingerprints:
"
+ var/incomplete = 0
+ for(var/fullprint in prints)
+ var/print = prints[fullprint]
+ if(is_complete_print(print))
+ . += "[print]
"
+ else
+ incomplete++
+ if(incomplete)
+ . += "[incomplete] incomplete fingerprints."
+ else
+ . += "
No fingerprints recorded.
"
+
+ var/list/fibers = fresh.fields["fibers"]
+ if(fibers.len)
+ . += "Fibers:
"
+ . += ""
+ for(var/fiber in fibers)
+ . += "- [fiber]
"
+ . += "
"
+ else
+ . += "
No fibers recorded."
+
+ var/list/bloods = fresh.fields["blood"]
+ if(bloods.len)
+ . += "Blood:
"
+ . += ""
+ for(var/blood in bloods)
+ . += "- DNA: [blood] Type: [bloods[blood]]
"
+ . += "
"
+ else
+ . += "
No blood recorded."
+
+/obj/machinery/computer/forensic_scanning/proc/add_record(var/datum/data/record/forensic/fresh)
+ var/datum/data/record/forensic/old = files[fresh.uid]
+ if(old)
+ fresh.merge(old)
+ fresh.fields["label"] = old.fields["label"]
+ files[fresh.uid] = fresh
+
+/obj/machinery/computer/forensic_scanning/proc/process_card(var/obj/item/weapon/f_card/card)
+ if(card.fingerprints)
+ usr << "\The [src] sucks in \the [card] and whirrs, scanning it."
+ var/found = 0
+ for(var/id in files)
+ var/datum/data/record/forensic/rec = files[id]
+ var/list/prints = rec.fields["fprints"]
+ for(var/master_print in card.fingerprints)
+ if(prints[master_print])
+ prints[master_print] = master_print
+ found = 1
+ if(found)
+ usr << "Complete match found."
+ else
+ usr << "No match found."
+ return 1
+ else
+ usr << "No fingerprints detected on [card]."
+ return 0
+
+//Takes a list of forensic records, with key being reference to object, and updates internal database.
+/obj/machinery/computer/forensic_scanning/proc/sync_data(var/list/newdata)
+ for(var/id in newdata)
+ var/datum/data/record/forensic/fresh = newdata[id]
+ add_record(fresh)
+
+/obj/machinery/computer/forensic_scanning/proc/get_filtered_set()
+ .= list()
+ for(var/id in files)
+ var/datum/data/record/forensic/cur = files[id]
+ var/add = 1
+ if(filters["name"])
+ add = 0
+ for(var/filter in filters["name"])
+ if(findtext(cur.fields["name"], filter))
+ add = 1
+ break
+
+ if(filters["area"])
+ add = 0
+ for(var/filter in filters["area"])
+ if(findtext(cur.fields["area"], filter))
+ add = 1
+ break
+
+ if(filters["fprints"])
+ add = 0
+ var/list/prints = cur.fields["fprints"]
+ for(var/pid in prints)
+ var/print = prints[pid]
+ if (is_complete_print(print))
+ for(var/filter in filters["fprints"])
+ if(findtext(print, filter))
+ add = 1
+ break
+
+ if(filters["fibers"])
+ add = 0
+ for(var/fiber in cur.fields["fibers"])
+ for(var/filter in filters["fibers"])
+ if(findtext(fiber, filter))
+ add = 1
+ break
+
+ if(filters["blood"])
+ add = 0
+ for(var/DNA in cur.fields["blood"])
+ for(var/filter in filters["blood"])
+ if(findtext(DNA, filter))
+ add = 1
+ break
+
+ if(filters["label"])
+ add = 0
+ for(var/filter in filters["label"])
+ if(cur.fields["label"] && findtext(cur.fields["label"], filter))
+ add = 1
+ break
+ if (add)
+ .+=cur
+
+/obj/machinery/computer/forensic_scanning/attack_hand(mob/user)
+ if(..())
+ return
+ user.set_machine(src)
+
+ var/dat
+ if(!authenticated)
+ dat += "{Log In}"
+ else
+ dat += "{Log Out}"
+ dat += " | "
+ dat += "Database"
+ dat += " | "
+ dat += "Record details"
+ dat += " | "
+ dat += "Scanning"
+ dat +="
"
+ switch(screen)
+ if("database") //Database screen
+ dat += "Search filters:
"
+ var/list/filternames = list("Object"="name", "Area"="area", "Fingerprints"="fprints", "Fibers"="fibers", "DNA"="blood", "Label"="label")
+ for(var/filter in filternames)
+ var/fname = filternames[filter]
+ dat += "
[filter]: [filters[fname] ? list2text(filters[fname], ",") : "All"]"
+
+ current_list = get_filtered_set()
+ dat+= "
"
+ if(current_list.len < 1)
+ dat += "No data matching your request found."
+ else
+ dat += ""
+ dat += "| Object | Area | Fingerprints | Fibers | Blood | Label |
"
+ for(var/datum/data/record/forensic/record in current_list)
+ dat += "| [record.fields["name"]] | "
+ dat += "[record.fields["area"]] | "
+ for(var/criteria in list("fprints", "fibers", "blood"))
+ var/list/data = record.fields[criteria]
+ dat += "[data.len ? data.len : "None"] | "
+ dat += "[record.fields["label"] ? record.fields["label"] : ""] | "
+ dat += "Delete | "
+ dat += "
"
+ dat += "
"
+ dat += "Print all listed
"
+
+ if("details") //Details screen
+ if(!current)
+ dat += "
NO RECORD SELECTED"
+ else
+ dat += get_printable_data(current)
+ dat += "Labels: "
+ dat += "[current.fields["label"] ? current.fields["label"] : "None"]
"
+ dat += "Print record
"
+
+ if("scan") //Scanning screen
+ dat += "Object: [scanning ? scanning.name : "-----"]
"
+ if (scanning)
+ if (scan_progress > 0)
+ dat += "Scan in progress."
+ dat += " Cancel
"
+ else
+ dat += "Scan
"
+ dat += "Insert fingerprint card here: -----"
+
+ user << browse(dat,"window=fscanner")
+ onclose(user,"fscanner")
+
+/obj/machinery/computer/forensic_scanning/Topic(href,href_list)
+ switch(href_list["operation"])
+ if("login")
+ var/mob/M = usr
+ if(istype(M,/mob/living/silicon) || allowed(M))
+ authenticated = 1
+ if("logout")
+ authenticated = 0
+ if("filter")
+ var/filterstr = stripped_input(usr,"Input the search criteria. Multiple values can be input, separated by a comma.", "Filter setting") as text|null
+ if(filterstr)
+ filters[href_list["filter"]] = text2list(filterstr,",")
+ else
+ filters[href_list["filter"]] = null
+ if("screen")
+ screen = href_list["screen"]
+ if("details")
+ if(href_list["identifier"])
+ screen = "details"
+ current = files[href_list["identifier"]]
+ else
+ usr << "No record found."
+ if("delete")
+ if(href_list["identifier"])
+ if(alert("Are you sure you want to delete this record?","Record deletion", "Yes", "No") == "Yes")
+ files.Remove(href_list["identifier"])
+ if(current && current.uid == href_list["identifier"])
+ current = null
+ if("label")
+ if(current)
+ var/label = stripped_input(usr,"Input the label for this record. Multiple values can be input, separated by a comma.", "Labeling record", current.fields["label"]) as text|null
+ current.fields["label"] = label
+ if("object")
+ if(scanning)
+ scanning.loc = get_turf(src)
+ scan_progress = -1
+ scanning = null
+ else
+ var/mob/M = usr
+ var/obj/item/I = M.get_active_hand()
+ if(I && istype(I))
+ if(istype(I, /obj/item/weapon/evidencebag))
+ scanning = I.contents[1]
+ scanning.loc = src
+ I.overlays.Cut()
+ I.w_class = 1
+ I.icon_state = "evidenceobj"
+ else
+ scanning = I
+ M.drop_item()
+ I.loc = src
+ else
+ usr << "Invalid object, rejected."
+ if("scan")
+ if(scanning)
+ scan_progress = 10
+ if("cancel")
+ scan_progress = -1
+ if("card")
+ var/mob/M = usr
+ var/obj/item/I = M.get_active_hand()
+ if(istype(I, /obj/item/weapon/f_card))
+ if(process_card(I))
+ M.drop_item()
+ del(I)
+ else
+ usr << "Invalid fingerprint card, rejected."
+ if("print")
+ if(current)
+ var/obj/item/weapon/paper/P = new(loc)
+ P.name = "\improper Forensics Data ([current.fields["name"]])"
+ P.icon_state = "paper_words"
+ P.info = "Forensics Database - [worldtime2text(world.time)]
"
+ P.info += get_printable_data(current)
+ if("printall")
+ var/obj/item/weapon/paper/P = new(loc)
+ P.name = "\improper Forensics Data"
+ P.icon_state = "paper_words"
+ P.info = "Forensics Database - [worldtime2text(world.time)]
"
+ for(var/datum/data/record/forensic/cur in current_list)
+ P.info += get_printable_data(cur)
+
+ updateUsrDialog()
+
+/obj/machinery/computer/forensic_scanning/process()
+ if (!..())
+ return
+ if(scanning)
+ if(scan_progress > 0)
+ scan_progress--
+ updateUsrDialog()
+ if(scan_progress == 0)
+ scan_progress = -1
+ ping("Scan complete.")
+ var/datum/data/record/forensic/fresh = new(scanning)
+ add_record(fresh)
+ updateUsrDialog()
\ No newline at end of file