//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_from_data(G.bloody_hands_data)) //only reduces the bloodiness of our gloves if the item wasn't already bloody
G.transfer_blood--
M.update_inv_gloves()
else if(M.bloody_hands > 1)
if(add_blood_from_data(M.bloody_hands_data))
M.bloody_hands--
M.update_inv_gloves()
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 & 32))
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 & 64))
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
/atom/proc/add_custom_fibers(var/txt, var/duplicates = TRUE)
if(!suit_fibers)
suit_fibers = list()
if(duplicates)
suit_fibers += txt
else
suit_fibers |= txt
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
circuit = "/obj/item/weapon/circuitboard/forensic_computer"
light_color = LIGHT_COLOR_RED
//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)
/obj/machinery/computer/forensic_scanning/attack_hand(mob/user)
if(..())
return
user.set_machine(src)
var/dat = ""
var/isai = FALSE
if(isAI(user))
isai = TRUE
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(HTML_SKELETON(dat),"window=scanner")
onclose(user,"scanner")
/obj/machinery/computer/forensic_scanning/Topic(href,href_list)
. = ..()
if(.)
return
switch(href_list["operation"])
if("login")
var/mob/M = usr
if(issilicon(M))
authenticated = 1
updateDialog()
if(allowed(M))
authenticated = 1
if("logout")
authenticated = 0
if("clear")
if(canclear)
temp = null
if("eject")
if(scanning)
scanning.forceMove(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(isgripper(I))
var/obj/item/weapon/gripper/G = I
if(G.wrapped)
scanning = G.wrapped //We add it as scanned object first because we'll lose the wrapped reference once we drop it.
G.drop_item(G.wrapped, src)
else
if(istype(I, /obj/item/weapon/storage/evidencebag) && I.contents.len)
var/obj/item/weapon/storage/evidencebag/EVB = I
scanning = EVB.contents[1]
EVB.remove_from_storage(scanning, src, TRUE, TRUE)
else
if(M.drop_item(I, src))
scanning = I
else
to_chat(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)
to_chat(usr, "ERROR: No prints/too many cards.")
if(card.loc == src)
card.forceMove(src.loc)
card = null
return
if(M.drop_item(I, src))
process_card()
else
to_chat(usr, "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
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
to_chat(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]]
 Fingerprints:
[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}
{Delete this Dossier}
{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.overlays += image(icon = P.icon, icon_state = "paper_words")
P.info = {"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]]
"
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]]
 Fingerprints:
[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
to_chat(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:
[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}
{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.overlays += image(icon = P.icon, icon_state = "paper_words")
P.info = {"Auxiliary Evidence Database
Consolidated data points: [outputs[3]]
"}
var/list/prints = outputs[4]
if(prints)
P.info += {" Fingerprints:
[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
to_chat(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]
"
var/is_scanner = istype(scanning, /obj/item/device/detective_scanner)
/*if(istype(scanning, /obj/item/device/pda))
var/obj/item/device/pda/the_pda = scanning
if(istype(the_pda.scanning_app,/datum/pda_app/cart/scanner/detective))
is_scanner = TRUE*/
if(is_scanner)
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.overlays += image(icon = P.icon, 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()
/obj/machinery/computer/forensic_scanning/ex_act()
return
/obj/machinery/computer/forensic_scanning/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))
var/obj/item/device/pda/the_pda = W
istype(the_pda.scanning_app,/datum/pda_app/cart/scanner/detective)
var/datum/pda_app/cart/scanner/detective/D = the_pda.scanning_app
if(D.stored_data)
for(var/atom in D)
var/list/data = D[atom]
add_data_master(atom,data[1],data[2],data[3],data[4])
D.stored_data = list()*/
return
/obj/machinery/computer/forensic_scanning/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*****
********************************/
/obj/machinery/computer/forensic_scanning/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***
********************************/
/obj/machinery/computer/forensic_scanning/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
/obj/machinery/computer/forensic_scanning/proc/process_card() //Same as above, but for fingerprint cards
if(card.fingerprints && !(card.amount > 1) && islist(card.fingerprints) && files && files.len)
to_chat(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)
to_chat(usr, "The machinery finds it can complete a match.")
else
to_chat(usr, "No match found.")
QDEL_NULL(card)
else
to_chat(usr, "ERROR: No prints/too many cards.")
if(card.loc == src)
card.forceMove(src.loc)
card = null
return
return
/obj/machinery/computer/forensic_scanning/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
/obj/machinery/computer/forensic_scanning/proc/delete_dossier(var/print) //Deletes a Dossier at a given location.
if(files && files.len)
files.Remove(print)
return
/obj/machinery/computer/forensic_scanning/detective
icon_state = "old"
name = "PowerScan Mk.I"