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