//gloves w_uniform wear_suit shoes atom/var/list/suit_fibers atom/proc/add_fibers(mob/living/carbon/human/M) if(M.gloves) if(M.gloves.transfer_blood) //bloodied gloves transfer blood to touched objects if(add_blood(M.gloves.bloody_hands_mob)) //only reduces the bloodiness of our gloves if the item wasn't already bloody M.gloves.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 & 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 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 = "High-Res Forensic Scanning Computer" icon_state = "forensic" var obj/item/scanning temp = "" canclear = 1 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. list/files //This holds objects (1) without prints, and their fibers(2) and blood(3). list/misc obj/item/weapon/f_card/card scan_data = "" scan_name = "" 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.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]}
" dat += "{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}

" dat += "[scan_data]" if(scan_data && !scan_process) dat += "
{Erase Data}" user << browse(dat,"window=scanner") onclose(user,"scanner") Topic(href,href_list) switch(href_list["operation"]) if("login") var/mob/M = usr if(istype(M,/mob/living/silicon)) authenticated = 1 updateDialog() return var/obj/item/weapon/card/id/I = M.equipped() if (I && istype(I)) if(src.check_access(I)) authenticated = 1 //usr << "\green Access Granted" //if(!authenticated) //usr << "\red Access Denied" 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.equipped() 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") var/mob/M = usr var/obj/item/I = M.equipped() 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") 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:
" var/i = 1 for(var/print in files) temp += "{Dossier [i]}
" i++ temp += "
{Insert Finger Print Card (To complete a Dossier)}


" else temp = "" if(misc && misc.len) temp += "Auxiliary Evidence Database

" temp += "This is where anything without fingerprints goes.

" for(var/atom in misc) var/list/data_entry = misc[atom] temp += "{[data_entry[3]]}
" if("record") canclear = 0 if(files) temp = "Criminal Evidence Database

" temp += "Consolidated data points: Dossier [files.Find(href_list["identifier"])]
" var/list/dossier = files[href_list["identifier"]] 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]) continue temp += "
" var/list/outputs = dossier[object] var/list/prints_len = outputs[1] temp += "Object: [outputs[4]]
" temp += " Fingerprints:
" temp += "    [prints_len.len] Unique fingerprints found.
" 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/j = 1, j <= blood.len, j++) var/list/templist2 = blood[j] temp += "      Type: [templist2[2]], DNA: [templist2[1]]
" temp += "
{Delete this Dossier}" temp += "
{Print}" else temp = "ERROR. Database not found!
" temp += "
{Return}" if("databaseprint") if(files) var/obj/item/weapon/paper/P = new(loc) P.name = "Database File (Dossier [files.Find(href_list["identifier"])])" P.overlays += "paper_words" P.info = "Criminal Evidence Database

" P.info += "Consolidated data points: Dossier [href_list["identifier"]]
" var/list/dossier = files[href_list["identifier"]] 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]) continue P.info += "
" var/list/outputs = dossier[object] var/list/prints_len = outputs[1] P.info += "Object: [outputs[4]]
" P.info += " Fingerprints:
" P.info += "    [prints_len.len] Unique fingerprints found.
" 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/j = 1, j <= blood.len, j++) var/list/templist2 = blood[j] P.info += "      Type: [templist2[2]], DNA: [templist2[1]]
" else usr << "ERROR. Database not found!
" if("auxiliary") canclear = 0 if(misc) temp = "Auxiliary Evidence Database

" var/list/outputs = misc[href_list["identifier"]] temp += "Consolidated data points: [outputs[3]]
" var/list/fibers = outputs[1] if(fibers && fibers.len) temp += " Fibers:
" for(var/j = 1, j <= fibers.len, j++) temp += "
      [fibers[j]]" var/list/blood = outputs[2] if(blood && blood.len) temp += " Blood:
" for(var/j = 1, j <= blood.len, j++) var/list/templist2 = blood[j] temp += "      Type: [templist2[2]], DNA: [templist2[1]]
" temp += "
{Delete This Record}" temp += "
{Print}" else temp = "ERROR. Database not found!
" temp += "
{Return}" if("auxiliaryprint") if(misc) var/obj/item/weapon/paper/P = new(loc) var/list/outputs = misc[href_list["identifier"]] P.name = "Auxiliary Database File ([outputs[3]])" P.overlays += "paper_words" P.info = "Auxiliary Evidence Database

" P.info += "Consolidated data points: [outputs[3]]
" var/list/fibers = outputs[1] if(fibers && fibers.len) P.info += " Fibers:
" for(var/j = 1, j <= fibers.len, j++) P.info += "
      [fibers[j]]" var/list/blood = outputs[2] if(blood && blood.len) P.info += " Blood:
" for(var/j = 1, j <= blood.len, j++) var/list/templist2 = blood[j] P.info += "      Type: [templist2[2]], DNA: [templist2[1]]
" 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 var/list/L = scanning.fingerprints scan_data += "Isolated [L.len] Fingerprints. Loaded into database.
" add_data(scanning) if(!scanning.suit_fibers) /*if(istype(scanning,/obj/item/device/detective_scanner)) var/obj/item/device/detective_scanner/scanner = scanning if(scanner.stored_name) scan_data += "Fibers/Materials Data - [scanner.stored_name]:
" for(var/data in scanner.stored_fibers) scan_data += "- [data]
" else scan_data += "No Fibers/Materials Data
" else*/ scan_data += "No Fibers/Materials Located
" else /*if(istype(scanning,/obj/item/device/detective_scanner)) var/obj/item/device/detective_scanner/scanner = scanning if(scanner.stored_name) scan_data += "Fibers/Materials Data - [scanner.stored_name]:
" for(var/data in scanner.stored_fibers) scan_data += "- [data]
" else scan_data += "No Fibers/Materials Data
"*/ scan_data += "Fibers/Materials Found:
" for(var/data in scanning.suit_fibers) scan_data += "- [data]
" if(istype(scanning,/obj/item/device/detective_scanner)) scan_data += "
Data transfered from Scanner to Database.
" add_data_scanner(scanning) else if(!scanning.fingerprints) scan_data += "
Add to Database?
" else temp = "Scan Failed: No Object" if("print") if(scan_data) temp = "Scan Data Printed." var/obj/item/weapon/paper/P = new(loc) P.name = "Scan Data ([scan_name])" P.info = "[scan_data]" P.overlays += "paper_words" else temp = "Print Failed: No Data" if("erase") scan_data = "" if("cancel") scan_process = 0 if("add") if(scanning) add_data(scanning) else temp = "Data Transfer Failed: No Object." updateUsrDialog() ex_act() return proc/add_data_scanner(var/obj/item/device/detective_scanner/W) if(W.stored) for(var/atom in W.stored) var/list/data = W.stored[atom] add_data_master(atom,data[1],data[2],data[3],data[4]) W.stored = 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) /******************************** *****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(!atom_fingerprints) //No prints 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/j = 1, j <= atom_suit_fibers.len, j++) //Fibers~~~ if(!fibers.Find(atom_suit_fibers[j])) //It isn't! Add! fibers += atom_suit_fibers[j] 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] return 1 var/list/templist[3] templist[1] = atom_suit_fibers templist[2] = atom_blood_DNA templist[3] = atom_name misc[atom_reference] = templist //Store it! return 0 //Has prints. if(!files) files = list() for(var/main_print in atom_fingerprints) var/list/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/j = 1, j < atom_suit_fibers.len, j++) //Fibers~~~ if(!fibers.Find(atom_suit_fibers[j])) //It isn't! Add! fibers += atom_suit_fibers[j] //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 data_point[2] = atom_suit_fibers data_point[3] = atom_blood_DNA 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 data_point[2] = atom_suit_fibers data_point[3] = atom_blood_DNA data_point[4] = atom_name var/list/new_file[1] new_file[1] = atom_fingerprints[main_print] 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") 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" obj/item/clothing/shoes/var track_blood = 0 mob/living/carbon/human/track_blood_mob track_blood_type mob/var bloody_hands = 0 mob/living/carbon/human/bloody_hands_mob track_blood mob/living/carbon/human/track_blood_mob track_blood_type obj/item/clothing/gloves/var transfer_blood = 0 mob/living/carbon/human/bloody_hands_mob obj/effect/decal/cleanable/var track_amt = 3 mob/blood_owner turf/Exited(mob/living/carbon/human/M) if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid)) if(!istype(src, /turf/space)) // Bloody tracks code starts here var/dofoot = 1 if(istype(M,/mob/living/simple_animal)) if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith))) dofoot = 0 if(dofoot) if(!istype(src, /turf/space)) // Bloody tracks code starts here if(M.track_blood > 0) M.track_blood-- src.add_bloody_footprints(M.track_blood_mob,1,M.dir,get_tracks(M),M.track_blood_type) else if(istype(M,/mob/living/carbon/human)) if(M.shoes) if(M.shoes.track_blood > 0) M.shoes.track_blood-- src.add_bloody_footprints(M.shoes.track_blood_mob,1,M.dir,M.shoes.name,M.shoes.track_blood_type) // And bloody tracks end here . = ..() turf/Entered(mob/living/carbon/human/M) if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid)) var/dofoot = 1 if(istype(M,/mob/living/simple_animal)) if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith))) dofoot = 0 if(dofoot) if(M.track_blood > 0) M.track_blood-- src.add_bloody_footprints(M.track_blood_mob,0,M.dir,get_tracks(M),M.track_blood_type) else if(istype(M,/mob/living/carbon/human)) if(M.shoes && !istype(src,/turf/space)) if(M.shoes.track_blood > 0) M.shoes.track_blood-- src.add_bloody_footprints(M.shoes.track_blood_mob,0,M.dir,M.shoes.name,M.shoes.track_blood_type) for(var/obj/effect/decal/cleanable/B in src) if(B:track_amt <= 0) continue if(B.type != /obj/effect/decal/cleanable/blood/tracks) if(istype(B, /obj/effect/decal/cleanable/xenoblood) || istype(B, /obj/effect/decal/cleanable/blood) || istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris)) var/track_type = "blood" if(istype(B, /obj/effect/decal/cleanable/xenoblood)) track_type = "xeno" else if(istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris)) track_type = "oil" if(istype(M,/mob/living/carbon/human)) if(M.shoes) M.shoes.add_blood(B.blood_owner) M.shoes.track_blood_mob = B.blood_owner M.shoes.track_blood = max(M.shoes.track_blood,8) M.shoes.track_blood_type = track_type else M.add_blood(B.blood_owner) M.track_blood_mob = B.blood_owner M.track_blood = max(M.track_blood,rand(4,8)) M.track_blood_type = track_type B.track_amt-- break . = ..() turf/proc/add_bloody_footprints(mob/living/carbon/human/M,leaving,d,info,bloodcolor) for(var/obj/effect/decal/cleanable/blood/tracks/T in src) if(T.dir == d && findtext(T.icon_state, bloodcolor)) if((leaving && T.icon_state == "steps2") || (!leaving && T.icon_state == "steps1")) T.desc = "These bloody footprints appear to have been made by [info]." if(!T.blood_DNA) T.blood_DNA = list() if(istype(M,/mob/living/carbon/human)) T.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type else if(istype(M,/mob/living/carbon/alien)) T.blood_DNA["UNKNOWN DNA"] = "X*" else if(istype(M,/mob/living/carbon/monkey)) T.blood_DNA["Non-human DNA"] = "A+" return var/obj/effect/decal/cleanable/blood/tracks/this = new(src) this.icon = 'footprints.dmi' var/preiconstate = "" if(info == "animal paws") preiconstate = "paw" else if(info == "alien claws") preiconstate = "claw" else if(info == "small alien feet") preiconstate = "paw" if(leaving) this.icon_state = "[bloodcolor][preiconstate]2" else this.icon_state = "[bloodcolor][preiconstate]1" this.dir = d if(bloodcolor == "blood") this.desc = "These bloody footprints appear to have been made by [info]." else if(bloodcolor == "xeno") this.desc = "These acidic bloody footprints appear to have been made by [info]." else if(bloodcolor == "oil") this.name = "oil" this.desc = "These oil footprints appear to have been made by [info]." if(istype(M,/mob/living/carbon/human)) if(!this.blood_DNA) this.blood_DNA = list() this.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type proc/get_tracks(mob/M) if(istype(M,/mob/living)) if(istype(M,/mob/living/carbon/human)) . = "human feet" else if(istype(M,/mob/living/carbon/monkey) || istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/crab)) . = "animal paws" else if(istype(M,/mob/living/silicon/robot)) . = "robot feet" else if(istype(M,/mob/living/carbon/alien/humanoid)) . = "alien claws" else if(istype(M,/mob/living/carbon/alien/larva)) . = "small alien feet" else . = "an unknown creature" proc/blood_incompatible(donor,receiver) var donor_antigen = copytext(donor,1,lentext(donor)) receiver_antigen = copytext(receiver,1,lentext(receiver)) donor_rh = findtext("+",donor) receiver_rh = findtext("+",receiver) if(donor_rh && !receiver_rh) return 1 switch(receiver_antigen) if("A") if(donor_antigen != "A" && donor_antigen != "O") return 1 if("B") if(donor_antigen != "B" && donor_antigen != "O") return 1 if("O") if(donor_antigen != "O") return 1 //AB is a universal receiver. return 0 /obj/item/weapon/rag name = "damp rag" desc = "For cleaning up messes, I suppose." w_class = 1 icon = 'toy.dmi' icon_state = "rag" afterattack(atom/A as obj|turf|area, mob/user as mob) if(istype(A)) user.visible_message("[user] starts to wipe down [A] with [src]!") if(do_after(user,30)) user.visible_message("[user] finishes wiping off the [A]!") A.clean_blood() return