//CONTAINS: Detective's Scanner
// TODO: Split everything into easy to manage procs.
/obj/item/detective_scanner
name = "forensic scanner"
desc = "Used to remotely scan objects and biomass for DNA and fingerprints. Can print a report of the findings."
icon = 'icons/goonstation/objects/objects.dmi'
icon_state = "detscanner"
w_class = WEIGHT_CLASS_NORMAL
item_state = "electronic"
flags = CONDUCT | NOBLUDGEON
slot_flags = SLOT_FLAG_BELT
origin_tech = "engineering=4;biotech=2;programming=5"
var/scanning = FALSE
var/list/log = list()
actions_types = list(/datum/action/item_action/print_forensic_report, /datum/action/item_action/clear_records)
/obj/item/detective_scanner/attack_self(mob/user)
var/search = input(user, "Enter name, fingerprint or blood DNA.", "Find record", "")
if(!search || user.stat || user.incapacitated())
return
search = lowertext(search) //This is here so that it doesn't run 'lowertext()' until the checks have passed.
var/name
var/fingerprint = "FINGERPRINT NOT FOUND"
var/dna = "BLOOD DNA NOT FOUND"
// I really, really wish I didn't have to split this into two seperate loops. But the datacore is awful.
for(var/record in GLOB.data_core.general) // Search in the 'general' datacore
var/datum/data/record/S = record
if(S && (search == lowertext(S.fields["fingerprint"]) || search == lowertext(S.fields["name"]))) // Get Fingerprint and Name
name = S.fields["name"]
fingerprint = S.fields["fingerprint"]
break
for(var/record in GLOB.data_core.medical) // Then search in the 'medical' datacore
var/datum/data/record/M = record
if(M && (search == lowertext(M.fields["b_dna"]) || name == M.fields["name"])) // Get Blood DNA
dna = M.fields["b_dna"]
if(fingerprint == "FINGERPRINT NOT FOUND") // We have searched for DNA, and so do not have the relevant information from the fingerprint records.
name = M.fields["name"]
for(var/gen_record in GLOB.data_core.general)
var/datum/data/record/S = gen_record
if(S && (name == S.fields["name"]))
fingerprint = S.fields["fingerprint"]
break
else //Eveything's been set, break the loop
break
if(name)
to_chat(user, "Match found in station records: [name]
\
Fingerprint: [fingerprint]
\
Blood DNA: [dna]")
else
to_chat(user, "No match found in station records.")
/obj/item/detective_scanner/ui_action_click(mob/user, actiontype)
if(actiontype == /datum/action/item_action/print_forensic_report)
print_scanner_report()
else
clear_scanner()
/obj/item/detective_scanner/proc/print_scanner_report()
if(length(log) && !scanning)
scanning = TRUE
to_chat(usr, "Printing report, please wait...")
playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1)
addtimer(CALLBACK(src, PROC_REF(make_paper), log), 10 SECONDS) // Create our paper
log = list() // Clear the logs
scanning = FALSE
else
to_chat(usr, "The scanner has no logs or is in use.")
/obj/item/detective_scanner/proc/make_paper(log) // Moved to a proc because 'spawn()' is evil
var/obj/item/paper/P = new(get_turf(src))
P.name = "paper- 'Scanner Report'"
P.info = "
Scanner Report
"
P.info += jointext(log, "
")
P.info += "
Notes:
"
P.info_links = P.info
if(ismob(loc))
var/mob/M = loc
M.put_in_hands(P)
to_chat(M, "Report printed. Log cleared.")
/obj/item/detective_scanner/proc/clear_scanner()
if(length(log) && !scanning)
log = list()
playsound(loc, 'sound/machines/ding.ogg', 40)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), usr, "Scanner logs cleared."), 1.5 SECONDS) //Timer so that it clears on the 'ding'
else
to_chat(usr, "The scanner has no logs or is in use.")
/obj/item/detective_scanner/attack()
return
/obj/item/detective_scanner/afterattack(atom/A, mob/user)
scan(A, user)
/obj/item/detective_scanner/proc/scan(atom/A, mob/user)
if(!scanning)
// Can remotely scan objects and mobs.
if(!(A in view(world.view, user)))
return
if(loc != user)
return
scanning = TRUE
user.visible_message("[user] points [src] at [A] and performs a forensic scan.",
"You scan [A]. The scanner is now analysing the results...")
// GATHER INFORMATION
//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(length(A.blood_DNA))
blood = A.blood_DNA.Copy()
if(length(A.suit_fibers))
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(length(A.fingerprints))
fingerprints = A.fingerprints.Copy()
// Only get reagents from non-mobs.
if(A.reagents && length(A.reagents.reagent_list))
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. Slowly display the results to the holder of the scanner.
var/found_something = FALSE
add_log("[station_time_timestamp()][get_timestamp()] - [target_name]", FALSE)
// Fingerprints
if(length(fingerprints))
sleep(30)
add_log("Prints:")
for(var/finger in fingerprints)
add_log("[finger]")
found_something = TRUE
// Blood
if(length(blood))
sleep(30)
add_log("Blood:")
found_something = TRUE
for(var/B in blood)
add_log("Type: [blood[B]] DNA: [B]")
//Fibers
if(length(fibers))
sleep(30)
add_log("Fibers:")
for(var/fiber in fibers)
add_log("[fiber]")
found_something = TRUE
//Reagents
if(length(reagents))
sleep(30)
add_log("Reagents:")
for(var/R in reagents)
add_log("Reagent: [R] Volume: [reagents[R]]")
found_something = TRUE
// Get a new user
var/mob/holder = null
if(ismob(loc))
holder = loc
if(!found_something)
add_log("# No forensic traces found #", FALSE) // Don't display this to the holder user
if(holder)
to_chat(holder, "Unable to locate any fingerprints, materials, fibers, or blood on [A]!")
else
if(holder)
to_chat(holder, "You finish scanning [A].")
add_log("---------------------------------------------------------", FALSE)
scanning = FALSE
/obj/item/detective_scanner/proc/add_log(msg, broadcast = TRUE)
if(scanning)
if(broadcast && ismob(loc))
var/mob/M = loc
to_chat(M, msg)
log += " [msg]"
else
CRASH("[src] \ref[src] is adding a log when it was never put in scanning mode!")
/proc/get_timestamp()
return time2text(world.time + 432000, ":ss")