diff --git a/code/modules/DetectiveWork/scanner.dm b/code/modules/DetectiveWork/scanner.dm
index 48d4812bac1..38c65445bb0 100644
--- a/code/modules/DetectiveWork/scanner.dm
+++ b/code/modules/DetectiveWork/scanner.dm
@@ -1,401 +1,173 @@
//CONTAINS: Detective's Scanner
+// TODO: Split everything into easy to manage procs.
/obj/item/device/detective_scanner
- name = "Scanner"
- desc = "Used to scan objects for DNA and fingerprints."
+ name = "forensic scanner"
+ desc = "Used to remotely scan objects and biomass for DNA and fingerprints. Can print a report of the findings."
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
+ var/scanning = 0
+ var/list/log = list()
- 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
+/obj/item/device/detective_scanner/attack_self(var/mob/user)
+ if(log.len && !scanning)
+ scanning = 1
+ user << "Printing report, please wait..."
- 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]'")
+ spawn(100)
- 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
+ // Create our paper
+ var/obj/item/weapon/paper/P = new(get_turf(src))
+ P.name = "paper- 'Scanner Report'"
+ P.info = "
Scanner Report
"
+ P.info += list2text(log, "
")
+ P.info += "
Notes:
"
+ P.info_links = P.info
- 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
+ if(ismob(loc))
+ var/mob/M = loc
+ M.put_in_hands(P)
+ M << "Report printed. Log cleared."
- add_fingerprint(user)
+ // Clear the logs
+ log = list()
+ scanning = 0
+ else
+ user << "The scanner has no logs or is in use."
+
+/obj/item/device/detective_scanner/attack(mob/living/M as mob, mob/user as mob)
+ return
- //Special case for blood splaters.
- 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
+/obj/item/device/detective_scanner/afterattack(atom/A, mob/user as mob, proximity)
+ scan(A, 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." : "."]" ,\
- "\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
+/obj/item/device/detective_scanner/proc/scan(var/atom/A, var/mob/user)
- 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
- return 0
-
-/obj/item/device/detective_scanner/forger
- var/list/custom_forgery[3]
- var/forging = 0
-
- New()
- ..()
- custom_forgery[1] = list()
- custom_forgery[2] = list()
- custom_forgery[3] = list()
-
- attack_self(var/mob/user as mob)
- var/list/customprints = list()
- var/list/customfiber = list()
- var/list/customblood = list()
- if(forging)
- user << "\red You are already forging evidence"
- return 0
- clear_forgery()
- //fingerprint loop
- while(1)
- var/print = html_encode(input(usr,"Please enter a custom fingerprint or hit cancel to finish fingerprints") as text|null)
- if(!usr.client)
- forging = 0
- break
- if(!print )
- break
- customprints[print] = print
- while(1)
- var/fiber = html_encode(input(usr,"Please enter a custom fiber/material trace or hit cancel to finish fibers/materials") as text|null)
- if(!usr.client)
- forging = 0
- break
- if(!fiber)
- break
- customfiber[fiber] = null
- while(1)
- var/blood = html_encode(input(usr,"Please enter a custom Blood DNA or hit cancel to finish forging") as text|null)
- var/bloodtype = html_encode(input(usr,"Please enter a custom Blood Type") as text|null)
- if(!usr.client)
- forging = 0
- break
- if(!blood)
- break
- customblood[blood] = bloodtype
- forging = 0
- if(!customprints.len && !customfiber.len)
- user << "\blue No forgery saved."
- return
- user << "\blue Forgery saved and will be tied to the next applicable scanned item."
- custom_forgery[1] = customprints ? customprints.Copy() : null
- custom_forgery[2] = customfiber ? customfiber.Copy() : null
- custom_forgery[3] = customblood ? customblood.Copy() : null
-
-//shameless copy pasting
- afterattack(atom/A as obj|turf|area, mob/user as mob)
- var/list/custom_finger = list()
- var/list/custom_fiber = list()
- var/list/custom_blood = list()
-
- if(custom_forgery)
- custom_finger = custom_forgery[1]
- custom_fiber = custom_forgery[2]
- custom_blood = custom_forgery[3]
-
- if(!in_range(A,user))
+ if(!scanning)
+ // Can remotely scan objects and mobs.
+ if(!in_range(A, user) && !(A in view(world.view, 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)
+ scanning = 1
+
+ user.visible_message("\The [user] points the [src.name] at \the [A] and performs a forensic scan.")
+ user << "You scan \the [A]. The scanner is now analysing the results..."
- //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
+ // GATHER INFORMATION
- //General
- if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA)
- if(!custom_finger.len && !custom_fiber.len && !custom_blood.len)
- 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
+ //Make our lists
+ var/list/fingerprints = list()
+ var/list/blood = list()
+ var/list/fibers = list()
+ var/list/reagents = list()
+
+ var/target_name = A.name
+
+ // Start gathering
+
+ if(A.blood_DNA && A.blood_DNA.len)
+ blood = A.blood_DNA.Copy()
+
+ if(A.suit_fibers && A.suit_fibers.len)
+ fibers = A.suit_fibers.Copy()
+
+ if(ishuman(A))
+
+ var/mob/living/carbon/human/H = A
+ if (istype(H.dna, /datum/dna) && !H.gloves)
+ fingerprints += md5(H.dna.uni_identity)
+
+ else if(!ismob(A))
+
+ if(A.fingerprints && A.fingerprints.len)
+ fingerprints = A.fingerprints.Copy()
+
+ // Only get reagents from non-mobs.
+ if(A.reagents && A.reagents.reagent_list.len)
+
+ for(var/datum/reagent/R in A.reagents.reagent_list)
+ reagents[R.name] = R.volume
+
+ // Get blood data from the blood reagent.
+ if(istype(R, /datum/reagent/blood))
+
+ if(R.data["blood_DNA"] && R.data["blood_type"])
+ var/blood_DNA = R.data["blood_DNA"]
+ var/blood_type = R.data["blood_type"]
+ blood[blood_DNA] = blood_type
+
+ // We gathered everything. Create a fork and slowly display the results to the holder of the scanner.
+
+ spawn(0)
+
+ var/found_something = 0
+ add_log("[worldtime2text()][get_timestamp()] - [target_name]", 0)
+
+ // Fingerprints
+ if(fingerprints && fingerprints.len)
+ sleep(30)
+ add_log("Prints:")
+ for(var/finger in fingerprints)
+ add_log("[finger]")
+ found_something = 1
+
+ // Blood
+ if (blood && blood.len)
+ sleep(30)
+ add_log("Blood:")
+ found_something = 1
+ for(var/B in blood)
+ add_log("Type: [blood[B]] DNA: [B]")
+
+ //Fibers
+ if(fibers && fibers.len)
+ sleep(30)
+ add_log("Fibers:")
+ for(var/fiber in fibers)
+ add_log("[fiber]")
+ found_something = 1
+
+ //Reagents
+ if(reagents && reagents.len)
+ sleep(30)
+ add_log("Reagents:")
+ for(var/R in reagents)
+ add_log("Reagent: [R] Volume: [reagents[R]]")
+ found_something = 1
+
+ // Get a new user
+ var/mob/holder = null
+ if(ismob(src.loc))
+ holder = src.loc
+
+ if(!found_something)
+ add_log("# No forensic traces found #", 0) // Don't display this to the holder user
+ if(holder)
+ holder << "Unable to locate any fingerprints, materials, fibers, or blood on \the [target_name]!"
else
- 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], loading custom forgery instead.",\
- "You hear a faint hum of electrical equipment.")
+ if(holder)
+ holder << "You finish scanning \the [target_name]."
-
- if(add_data(A))
- user << "\blue Object already in internal memory. Consolidating data..."
+ add_log("---------------------------------------------------------", 0)
+ scanning = 0
return
+/obj/item/device/detective_scanner/proc/add_log(var/msg, var/broadcast = 1)
+ if(scanning)
+ if(broadcast && ismob(loc))
+ var/mob/M = loc
+ M << msg
+ log += " [msg]"
+ else
+ CRASH("[src] \ref[src] is adding a log when it was never put in scanning mode!")
- //PRINTS
- if(!A.fingerprints || !A.fingerprints.len)
- if(A.fingerprints)
- del(A.fingerprints)
- if(custom_finger.len)
- user << "\blue Isolated [custom_finger.len] fingerprints: Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
- user << "\blue Found [custom_finger.len] intact prints"
- for(var/i in custom_finger)
- user << "\blue [i]"
- else if(A.fingerprints && A.fingerprints.len)
- 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(custom_fiber.len)
- user << "\blue Forged Fibers/Materials Data Found: Scan with Hi-Res Forensic Scanner to retrieve."
- else if(A.suit_fibers)
- user << "\blue Fibers/Materials Data Stored: Scan with Hi-Res Forensic Scanner to retrieve."
-
-
- //Blood
- if(custom_blood.len)
- user << "\blue Forged Blood found. Analysing..."
- spawn(15)
- for(var/blood in custom_blood)
- user << "Blood type: \red [custom_blood[blood]] \t \black DNA: \red [blood]"
- else 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]"
- return
-
- add_data(atom/A as mob|obj|turf|area)
- //I love associative lists.
- var/list/data_entry = stored["\ref [A]"]
- var/list/custom_finger = list()
- var/list/custom_fiber = list()
- var/list/custom_blood = list()
-
- if(custom_forgery)
- custom_finger = custom_forgery[1]
- custom_fiber = custom_forgery[2]
- custom_blood = custom_forgery[3]
-
- if(islist(data_entry)) //Yay, it was already stored!
- //Merge the fingerprints.
- var/list/data_prints = data_entry[1]
- if(custom_finger.len)
- for(var/print in custom_finger)
- var/merged_print = data_prints[print]
- if(!merged_print)
- data_prints[print] = custom_finger
- else
- data_prints[print] = stringmerge(data_prints[print],custom_finger[print])
- else
- 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(custom_fiber.len)
- for(var/j = 1, j <= custom_fiber.len, j++) //Fibers~~~
- if(!fibers.Find(custom_fiber[j])) //It isn't! Add!
- fibers += custom_fiber[j]
-
- else 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(custom_blood.len)
- for(var/main_blood in custom_blood)
- if(!blood[main_blood])
- blood[main_blood] = custom_blood[blood]
- else 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!
- if(custom_finger.len || custom_fiber.len || custom_blood.len)
- sum_list[1] = custom_finger ? custom_finger.Copy() : null
- sum_list[2] = custom_fiber ? custom_fiber.Copy() : null
- sum_list[3] = custom_blood ? custom_blood.Copy() : null
- else
- 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
- clear_forgery()
- return 0
-
- proc/clear_forgery()
- if(custom_forgery.len)
- custom_forgery[1] = list()
- custom_forgery[2] = list()
- custom_forgery[3] = list()
\ No newline at end of file
+/proc/get_timestamp()
+ return time2text(world.time + 432000, ":ss")
\ No newline at end of file