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