From 9955852ab964227fdbed725307a145583bb90e4f Mon Sep 17 00:00:00 2001 From: Cael_Aislinn Date: Mon, 5 Nov 2012 01:16:21 +1000 Subject: [PATCH] retrack detective files Signed-off-by: Cael_Aislinn --- baystation12.dme | 3 +- code/modules/DetectiveWork/detective_work.dm | 626 ++++++++++++++++++ code/modules/DetectiveWork/evidence.dm | 80 +++ .../DetectiveWork/footprints_and_rag.dm | 200 ++++++ code/modules/DetectiveWork/scanner.dm | 172 +++++ 5 files changed, 1080 insertions(+), 1 deletion(-) create mode 100644 code/modules/DetectiveWork/detective_work.dm create mode 100644 code/modules/DetectiveWork/evidence.dm create mode 100644 code/modules/DetectiveWork/footprints_and_rag.dm create mode 100644 code/modules/DetectiveWork/scanner.dm diff --git a/baystation12.dme b/baystation12.dme index 3cc437dad63..0ec465a3de5 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -252,7 +252,6 @@ #define FILE_DIR "interface" #define FILE_DIR "maps" #define FILE_DIR "maps/RandomZLevels" -#define FILE_DIR "music" #define FILE_DIR "sound" #define FILE_DIR "sound/AI" #define FILE_DIR "sound/ambience" @@ -267,6 +266,8 @@ #define FILE_DIR "sound/violin" #define FILE_DIR "sound/voice" #define FILE_DIR "sound/weapons" +#define FILE_DIR "tools" +#define FILE_DIR "tools/Redirector" // END_FILE_DIR // BEGIN_PREFERENCES diff --git a/code/modules/DetectiveWork/detective_work.dm b/code/modules/DetectiveWork/detective_work.dm new file mode 100644 index 00000000000..2dc409beb6f --- /dev/null +++ b/code/modules/DetectiveWork/detective_work.dm @@ -0,0 +1,626 @@ +//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 & 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 = "\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.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 + 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

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

" + temp += "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) + P.name = "\improper Database File (Dossier [files.Find(href_list["identifier"])])" + P.overlays += "paper_words" + P.info = "Criminal Evidence Database

" + var/list/dossier = files[href_list["identifier"]] + 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.overlays += "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.overlays += "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)) + 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() + 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 + templist[2] = atom_blood_DNA + templist[3] = atom_name + templist[4] = atom_fingerprints + 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 + 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[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/evidence.dm b/code/modules/DetectiveWork/evidence.dm new file mode 100644 index 00000000000..a5aa48dd5c2 --- /dev/null +++ b/code/modules/DetectiveWork/evidence.dm @@ -0,0 +1,80 @@ +//CONTAINS: Evidence bags + +/obj/item/weapon/evidencebag + name = "evidence bag" + desc = "An empty evidence bag." + icon = 'icons/obj/storage.dmi' + icon_state = "evidenceobj" + w_class = 1 + +/obj/item/weapon/evidencebag/afterattack(obj/item/O, mob/user as mob) + if(!in_range(O,user)) + return + + if(istype(O, /obj/item/weapon/storage)) + return ..() + + if(!istype(O) || O.anchored == 1) + user << "You can't put that inside \the [src]!" + return ..() + + if(istype(O, /obj/item/weapon/evidencebag)) + user << "You find putting an evidence bag in another evidence bag to be slightly absurd." + return + + if(contents.len) + user << "\The [src] already has something inside it." + return ..() + + if(!isturf(O.loc)) //If it isn't on the floor. Do some checks to see if it's in our hands or a box. Otherwise give up. + if(istype(O.loc,/obj/item/weapon/storage)) //in a container. + var/obj/item/weapon/storage/U = O.loc + user.client.screen -= O + U.contents.Remove(O) + else if(user.l_hand == O) //in a hand + user.drop_l_hand() + else if(user.r_hand == O) //in a hand + user.drop_r_hand() + else + return + + user.visible_message("\The [user] puts \a [O] into \a [src]", "You put \the [O] inside \the [src].",\ + "You hear a rustle as someone puts something into a plastic bag.") + icon_state = "evidence" + var/image/I = image("icon"=O, "layer"=FLOAT_LAYER) //take a snapshot. (necessary to stop the underlays appearing under our inventory-HUD slots ~Carn + underlays += I + desc = "An evidence bag containing \a [O]. [O.desc]" + O.loc = src + w_class = O.w_class + return + + +/obj/item/weapon/evidencebag/attack_self(mob/user as mob) + if (contents.len) + var/obj/item/I = contents[1] + user.visible_message("\The [user] takes \a [I] out of \a [src]", "You take \the [I] out of \the [src].",\ + "You hear someone rustle around in a plastic bag, and remove something.") + underlays = null //remove the underlays + user.put_in_hands(I) + w_class = 1 + icon_state = "evidenceobj" + desc = "An empty evidence bag." + + else + user << "\The [src] is empty." + icon_state = "evidenceobj" + return + +/obj/item/weapon/storage/box/evidence + name = "evidence bag box" + desc = "A box claiming to contain evidence bags." + New() + new /obj/item/weapon/evidencebag(src) + new /obj/item/weapon/evidencebag(src) + new /obj/item/weapon/evidencebag(src) + new /obj/item/weapon/evidencebag(src) + new /obj/item/weapon/evidencebag(src) + new /obj/item/weapon/evidencebag(src) + new /obj/item/weapon/f_card(src) + ..() + return \ No newline at end of file diff --git a/code/modules/DetectiveWork/footprints_and_rag.dm b/code/modules/DetectiveWork/footprints_and_rag.dm new file mode 100644 index 00000000000..4aceed22ec0 --- /dev/null +++ b/code/modules/DetectiveWork/footprints_and_rag.dm @@ -0,0 +1,200 @@ +/* +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 && istype(M.shoes,/obj/item/clothing/shoes)) + var/obj/item/clothing/shoes/S = M.shoes + if(S.track_blood > 0) + S.track_blood-- + src.add_bloody_footprints(S.track_blood_mob,1,M.dir,S.name,S.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(M.shoes,/obj/item/clothing/shoes) && !istype(src,/turf/space)) + var/obj/item/clothing/shoes/S = M.shoes + if(S.track_blood > 0) + S.track_blood-- + src.add_bloody_footprints(S.track_blood_mob,0,M.dir,S.name,S.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 && istype(M.shoes,/obj/item/clothing/shoes)) + var/obj/item/clothing/shoes/S = M.shoes + S.add_blood(B.blood_owner) + S.track_blood_mob = B.blood_owner + S.track_blood = max(S.track_blood,8) + S.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 = 'icons/effects/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/reagent_containers/glass/rag + name = "damp rag" + desc = "For cleaning up messes, you suppose." + w_class = 1 + icon = 'icons/obj/toy.dmi' + icon_state = "rag" + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list(5) + volume = 5 + can_be_placed_into = null + + attack(atom/target as obj|turf|area, mob/user as mob , flag) + if(ismob(target) && target.reagents && reagents.total_volume) + user.visible_message("\red \The [target] has been smothered with \the [src] by \the [user]!", "\red You smother \the [target] with \the [src]!", "You hear some struggling and muffled cries of surprise") + src.reagents.reaction(target, INGEST) + spawn(5) src.reagents.clear_reagents() + return + else + ..() + + afterattack(atom/A as obj|turf|area, mob/user as mob) + if(istype(A) && src in user) + 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 + + examine() + if (!usr) + return + usr << "That's \a [src]." + usr << desc + return \ No newline at end of file diff --git a/code/modules/DetectiveWork/scanner.dm b/code/modules/DetectiveWork/scanner.dm new file mode 100644 index 00000000000..09eb646a448 --- /dev/null +++ b/code/modules/DetectiveWork/scanner.dm @@ -0,0 +1,172 @@ +//CONTAINS: Detective's Scanner + +/obj/item/device/detective_scanner + name = "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 | USEDELAY + 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." + return 0 + if (( !( istype(M.dna, /datum/dna) ) || M.gloves) ) + user << "\blue No fingerprints found on [M]" + 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) + if(!in_range(A,user)) + 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.\"" + return + + add_fingerprint(user) + + + //Special case for blood splaters. + if (istype(A, /obj/effect/decal/cleanable/blood) || istype(A, /obj/effect/rune)) + if(!isnull(A.blood_DNA)) + for(var/blood in A.blood_DNA) + user << "\blue Blood type: [A.blood_DNA[blood]]\nDNA: [blood]" + 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.") + return 0 + + if(add_data(A)) + user << "\blue Object already in internal memory. Consolidating data..." + 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." + + //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.") + 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.") + return 0 + return + + proc/add_data(atom/A as mob|obj|turf|area) + //I love hashtables. + 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 + sum_list[2] = A.suit_fibers + sum_list[3] = A.blood_DNA + sum_list[4] = "\The [A] in \the [get_area(A)]" + stored["\ref [A]"] = sum_list + return 0 \ No newline at end of file