//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"