Porting forensics from Aurora.

This commit is contained in:
Zuhayr
2015-09-05 23:08:14 +09:30
parent eaab614221
commit 565e39a28f
49 changed files with 1198 additions and 835 deletions

View File

@@ -80,7 +80,7 @@
#define COLOR_PINK "#FF00FF" #define COLOR_PINK "#FF00FF"
#define COLOR_PURPLE "#800080" #define COLOR_PURPLE "#800080"
#define COLOR_ORANGE "#FF9900" #define COLOR_ORANGE "#FF9900"
#define COLOR_LUMINOL "#66FFFF"
// Shuttles. // Shuttles.
// These define the time taken for the shuttle to get to the space station, and the time before it leaves again. // These define the time taken for the shuttle to get to the space station, and the time before it leaves again.

View File

@@ -43,6 +43,20 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
group = "Security" group = "Security"
hidden = 1 hidden = 1
/datum/supply_packs/forensics
name = "Auxiliary forensic tools"
contains = list(/obj/item/weapon/forensics/sample_kit,
/obj/item/weapon/forensics/sample_kit/powder,
/obj/item/weapon/storage/box/swabs,
/obj/item/weapon/storage/box/swabs,
/obj/item/weapon/storage/box/swabs,
/obj/item/weapon/storage/box/slides,
/obj/item/weapon/reagent_containers/spray/luminol)
cost = 30
containertype = /obj/structure/closet/crate
containername = "Auxiliary forensic tools"
group = "Security"
/datum/supply_packs/food /datum/supply_packs/food
name = "Kitchen supply crate" name = "Kitchen supply crate"
contains = list(/obj/item/weapon/reagent_containers/food/condiment/flour, contains = list(/obj/item/weapon/reagent_containers/food/condiment/flour,

View File

@@ -6,12 +6,14 @@
var/list/fingerprintshidden var/list/fingerprintshidden
var/fingerprintslast = null var/fingerprintslast = null
var/list/blood_DNA var/list/blood_DNA
var/was_bloodied
var/blood_color var/blood_color
var/last_bumped = 0 var/last_bumped = 0
var/pass_flags = 0 var/pass_flags = 0
var/throwpass = 0 var/throwpass = 0
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
var/simulated = 1 //filter for actions - used by lighting overlays var/simulated = 1 //filter for actions - used by lighting overlays
var/fluorescent // Shows up under a UV light.
///Chemistry. ///Chemistry.
var/datum/reagents/reagents = null var/datum/reagents/reagents = null
@@ -23,6 +25,9 @@
//Detective Work, used for the duplicate data points kept in the scanners //Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom var/list/original_atom
/atom/proc/reveal_blood()
return
/atom/proc/assume_air(datum/gas_mixture/giver) /atom/proc/assume_air(datum/gas_mixture/giver)
return null return null
@@ -304,7 +309,7 @@ its easier to just keep the beam vertical.
fingerprints = list() fingerprints = list()
//Hash this shit. //Hash this shit.
var/full_print = md5(H.dna.uni_identity) var/full_print = H.get_full_print()
// Add the fingerprints // Add the fingerprints
// //
@@ -387,6 +392,7 @@ its easier to just keep the beam vertical.
if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it.
blood_DNA = list() blood_DNA = list()
was_bloodied = 1
blood_color = "#A10808" blood_color = "#A10808"
if(istype(M)) if(istype(M))
if (!istype(M.dna, /datum/dna)) if (!istype(M.dna, /datum/dna))
@@ -406,10 +412,10 @@ its easier to just keep the beam vertical.
if(toxvomit) if(toxvomit)
this.icon_state = "vomittox_[pick(1,4)]" this.icon_state = "vomittox_[pick(1,4)]"
/atom/proc/clean_blood() /atom/proc/clean_blood()
if(!simulated) if(!simulated)
return return
fluorescent = 0
src.germ_level = 0 src.germ_level = 0
if(istype(blood_DNA, /list)) if(istype(blood_DNA, /list))
blood_DNA = null blood_DNA = null

View File

@@ -111,12 +111,11 @@
H.equip_to_slot_or_del(new /obj/item/weapon/flame/lighter/zippo(H), slot_l_store) H.equip_to_slot_or_del(new /obj/item/weapon/flame/lighter/zippo(H), slot_l_store)
if(H.backbag == 1)//Why cant some of these things spawn in his office? if(H.backbag == 1)//Why cant some of these things spawn in his office?
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_l_hand) H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_l_hand)
H.equip_to_slot_or_del(new /obj/item/device/detective_scanner(H), slot_r_store)
else else
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_in_backpack) H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/device/detective_scanner(H), slot_in_backpack)
if(H.mind.role_alt_title && H.mind.role_alt_title == "Forensic Technician") if(H.mind.role_alt_title && H.mind.role_alt_title == "Forensic Technician")
H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/forensics/blue(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/forensics/blue(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/weapon/storage/briefcase/crimekit, slot_r_hand)
else else
H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_trench(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_trench(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/clothing/head/det(H), slot_head) H.equip_to_slot_or_del(new /obj/item/clothing/head/det(H), slot_head)

View File

@@ -15,6 +15,4 @@
return src.attack_hand(user) return src.attack_hand(user)
/obj/machinery/button/attackby(obj/item/weapon/W, mob/user as mob) /obj/machinery/button/attackby(obj/item/weapon/W, mob/user as mob)
if(istype(W, /obj/item/device/detective_scanner))
return
return src.attack_hand(user) return src.attack_hand(user)

View File

@@ -24,22 +24,6 @@
user << "Error, no route to host." user << "Error, no route to host."
/obj/machinery/button/remote/attackby(obj/item/weapon/W, mob/user as mob) /obj/machinery/button/remote/attackby(obj/item/weapon/W, mob/user as mob)
/* For later implementation
if (istype(W, /obj/item/weapon/screwdriver))
{
if(wiresexposed)
icon_state = "doorctrl0"
wiresexposed = 0
else
icon_state = "doorctrl-open"
wiresexposed = 1
return
}
*/
if(istype(W, /obj/item/device/detective_scanner))
return
return src.attack_hand(user) return src.attack_hand(user)
/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) /obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user)

View File

@@ -750,7 +750,7 @@ About the new airlock wires panel:
if(src.isElectrified()) if(src.isElectrified())
if(src.shock(user, 75)) if(src.shock(user, 75))
return return
if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll)) if(istype(C, /obj/item/taperoll))
return return
src.add_fingerprint(user) src.add_fingerprint(user)

View File

@@ -202,8 +202,6 @@
..() ..()
/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) /obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob)
if(istype(I, /obj/item/device/detective_scanner))
return
src.add_fingerprint(user) src.add_fingerprint(user)
if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name()) if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name())

View File

@@ -72,8 +72,6 @@
// src.sd_SetLuminosity(0) // src.sd_SetLuminosity(0)
/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob) /obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/device/detective_scanner))
return
if (istype(W, /obj/item/weapon/screwdriver)) if (istype(W, /obj/item/weapon/screwdriver))
add_fingerprint(user) add_fingerprint(user)
src.disable = !src.disable src.disable = !src.disable

View File

@@ -22,6 +22,19 @@ var/global/list/image/splatter_cache=list()
var/amount = 5 var/amount = 5
var/drytime var/drytime
/obj/effect/decal/cleanable/blood/reveal_blood()
if(!fluorescent)
fluorescent = 1
basecolor = COLOR_LUMINOL
update_icon()
/obj/effect/decal/cleanable/blood/clean_blood()
fluorescent = 0
if(invisibility != 100)
invisibility = 100
amount = 0
processing_objects -= src
/obj/effect/decal/cleanable/blood/Destroy() /obj/effect/decal/cleanable/blood/Destroy()
for(var/datum/disease/D in viruses) for(var/datum/disease/D in viruses)
D.cure(0) D.cure(0)

View File

@@ -29,6 +29,13 @@ var/global/list/image/fluidtrack_cache=list()
src.basecolor=_color src.basecolor=_color
src.wet=_wet src.wet=_wet
/obj/effect/decal/cleanable/blood/tracks/reveal_blood()
if(!fluorescent)
if(stack && stack.len)
for(var/datum/fluidtrack/track in stack)
track.basecolor = COLOR_LUMINOL
..()
// Footprints, tire trails... // Footprints, tire trails...
/obj/effect/decal/cleanable/blood/tracks /obj/effect/decal/cleanable/blood/tracks
amount = 0 amount = 0

View File

@@ -1,6 +1,9 @@
/obj/effect/decal/cleanable /obj/effect/decal/cleanable
var/list/random_icon_states = list() var/list/random_icon_states = list()
/obj/effect/decal/cleanable/clean_blood()
qdel(src)
/obj/effect/decal/cleanable/New() /obj/effect/decal/cleanable/New()
if (random_icon_states && length(src.random_icon_states) > 0) if (random_icon_states && length(src.random_icon_states) > 0)
src.icon_state = pick(src.random_icon_states) src.icon_state = pick(src.random_icon_states)

View File

@@ -494,6 +494,12 @@ var/list/global/slot_flags_enumeration = list(
var/obj/item/clothing/gloves/G = src var/obj/item/clothing/gloves/G = src
G.transfer_blood = 0 G.transfer_blood = 0
/obj/item/reveal_blood()
if(was_bloodied && !fluorescent)
fluorescent = 1
blood_color = COLOR_LUMINOL
blood_overlay.color = COLOR_LUMINOL
update_icon()
/obj/item/add_blood(mob/living/carbon/human/M as mob) /obj/item/add_blood(mob/living/carbon/human/M as mob)
if (!..()) if (!..())

View File

@@ -281,7 +281,6 @@
new /obj/item/weapon/storage/box/evidence(src) new /obj/item/weapon/storage/box/evidence(src)
new /obj/item/device/radio/headset/headset_sec(src) new /obj/item/device/radio/headset/headset_sec(src)
new /obj/item/device/radio/headset/headset_sec/alt(src) new /obj/item/device/radio/headset/headset_sec/alt(src)
new /obj/item/device/detective_scanner(src)
new /obj/item/clothing/suit/storage/vest/detective(src) new /obj/item/clothing/suit/storage/vest/detective(src)
new /obj/item/ammo_magazine/c45m/rubber(src) new /obj/item/ammo_magazine/c45m/rubber(src)
new /obj/item/ammo_magazine/c45m/rubber(src) new /obj/item/ammo_magazine/c45m/rubber(src)

View File

@@ -14,6 +14,11 @@
var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to
var/dirt = 0 var/dirt = 0
/turf/simulated/clean_blood()
for(var/obj/effect/decal/cleanable/blood/B in contents)
B.fluorescent = 0
B.invisibility = 100
/turf/simulated/New() /turf/simulated/New()
..() ..()
if(istype(loc, /area/chapel)) if(istype(loc, /area/chapel))

View File

@@ -216,3 +216,6 @@ var/const/enterloopsanity = 100
if(A.density && !(A.flags & ON_BORDER)) if(A.density && !(A.flags & ON_BORDER))
return 1 return 1
return 0 return 0
/turf/proc/update_blood_overlays()
return

View File

@@ -2,6 +2,7 @@
name = "clothing" name = "clothing"
siemens_coefficient = 0.9 siemens_coefficient = 0.9
var/list/species_restricted = null //Only these species can wear this kit. var/list/species_restricted = null //Only these species can wear this kit.
var/gunshot_residue //Used by forensics.
/* /*
Sprites used when the clothing item is refit. This is done by setting icon_override. Sprites used when the clothing item is refit. This is done by setting icon_override.
@@ -15,6 +16,11 @@
/obj/item/clothing/proc/update_clothing_icon() /obj/item/clothing/proc/update_clothing_icon()
return return
// Aurora forensics port.
/obj/item/clothing/clean_blood()
..()
gunshot_residue = null
//BS12: Species-restricted clothing check. //BS12: Species-restricted clothing check.
/obj/item/clothing/mob_can_equip(M as mob, slot) /obj/item/clothing/mob_can_equip(M as mob, slot)

View File

@@ -96,7 +96,7 @@
item_state = "det_suit" item_state = "det_suit"
blood_overlay_type = "coat" blood_overlay_type = "coat"
body_parts_covered = UPPER_TORSO|ARMS body_parts_covered = UPPER_TORSO|ARMS
allowed = list(/obj/item/weapon/tank/emergency_oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter,/obj/item/device/detective_scanner,/obj/item/device/taperecorder) allowed = list(/obj/item/weapon/tank/emergency_oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter,/obj/item/device/taperecorder)
armor = list(melee = 50, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0) armor = list(melee = 50, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0)
/obj/item/clothing/suit/storage/det_trench/grey /obj/item/clothing/suit/storage/det_trench/grey
@@ -109,7 +109,7 @@
desc = "A forensics technician jacket." desc = "A forensics technician jacket."
item_state = "det_suit" item_state = "det_suit"
body_parts_covered = UPPER_TORSO|ARMS body_parts_covered = UPPER_TORSO|ARMS
allowed = list(/obj/item/weapon/tank/emergency_oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/detective_scanner,/obj/item/device/taperecorder) allowed = list(/obj/item/weapon/tank/emergency_oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/taperecorder)
armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0)
/obj/item/clothing/suit/storage/forensics/red /obj/item/clothing/suit/storage/forensics/red

View File

View File

@@ -1,3 +1,7 @@
/obj/item/weapon/forensics
icon = 'icons/obj/forensics.dmi'
w_class = 1
//This is the output of the stringpercent(print) proc, and means about 80% of //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) //the print must be there for it to be complete. (Prints are 32 digits)
var/const/FINGERPRINT_COMPLETE = 6 var/const/FINGERPRINT_COMPLETE = 6

View File

@@ -0,0 +1,153 @@
//DNA machine
/obj/machinery/dnaforensics
name = "DNA analyzer"
desc = "A high tech machine that is designed to read DNA samples properly."
icon = 'icons/obj/forensics.dmi'
icon_state = "dnaopen"
anchored = 1
density = 1
var/obj/item/weapon/forensics/swab/bloodsamp = null
var/closed = 0
var/scanning = 0
var/scanner_progress = 0
var/scanner_rate = 2.50
var/last_process_worldtime = 0
var/report_num = 0
/obj/machinery/dnaforensics/attackby(var/obj/item/W, mob/user as mob)
if(bloodsamp)
user << "<span class='warning'>There is already a sample in the machine.</span>"
return
if(closed)
user << "<span class='warning'>Open the cover before inserting the sample.</span>"
return
var/obj/item/weapon/forensics/swab/swab = W
if(istype(swab) && swab.is_used())
user.unEquip(W)
src.bloodsamp = swab
swab.loc = src
user << "<span class='notice'>You insert \the [W] into \the [src].</span>"
else
user << "<span class='warning'>\The [src] only accepts used swabs.</span>"
return
/obj/machinery/dnaforensics/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null)
if(stat & (NOPOWER)) return
if(user.stat || user.restrained()) return
var/list/data = list()
data["scan_progress"] = round(scanner_progress)
data["scanning"] = scanning
data["bloodsamp"] = (bloodsamp ? bloodsamp.name : "")
data["bloodsamp_desc"] = (bloodsamp ? (bloodsamp.desc ? bloodsamp.desc : "No information on record.") : "")
data["lidstate"] = closed
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
if (!ui)
ui = new(user, src, ui_key, "dnaforensics.tmpl", "QuikScan DNA Analyzer", 540, 326)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/dnaforensics/Topic(href, href_list)
if(..()) return 1
if(stat & (NOPOWER))
return 0 // don't update UIs attached to this object
if(href_list["scanItem"])
if(scanning)
scanning = 0
else
if(bloodsamp)
if(closed == 1)
scanner_progress = 0
scanning = 1
usr << "<span class='notice'>Scan initiated.</span>"
update_icon()
else
usr << "<span class='notice'>Please close sample lid before initiating scan.</span>"
else
usr << "<span class='warning'>Insert an item to scan.</span>"
if(href_list["ejectItem"])
if(bloodsamp)
bloodsamp.forceMove(src.loc)
bloodsamp = null
if(href_list["toggleLid"])
toggle_lid()
return 1
/obj/machinery/dnaforensics/process()
if(scanning)
if(!bloodsamp || bloodsamp.loc != src)
bloodsamp = null
scanning = 0
else if(scanner_progress >= 100)
complete_scan()
return
else
//calculate time difference
var/deltaT = (world.time - last_process_worldtime) * 0.1
scanner_progress = min(100, scanner_progress + scanner_rate * deltaT)
last_process_worldtime = world.time
/obj/machinery/dnaforensics/proc/complete_scan()
src.visible_message("<span class='notice'>\icon[src] makes an insistent chime.</span>", 2)
update_icon()
if(bloodsamp)
var/obj/item/weapon/paper/P = new(src)
P.name = "[src] report #[++report_num]: [bloodsamp.name]"
P.stamped = list(/obj/item/weapon/stamp)
P.overlays = list("paper_stamped")
//dna data itself
var/data = "No scan information available."
if(bloodsamp.dna != null)
data = "Spectometric analysis on provided sample has determined the presence of [bloodsamp.dna.len] strings of DNA.<br><br>"
for(var/blood in bloodsamp.dna)
data += "\blue Blood type: [bloodsamp.dna[blood]]<br>\nDNA: [blood]<br><br>"
else
data += "No DNA found.<br>"
P.info = "<b>[src] analysis report #[report_num]</b><br>"
P.info += "<b>Scanned item:</b><br>[bloodsamp.name]<br>[bloodsamp.desc]<br><br>" + data
P.forceMove(src.loc)
P.update_icon()
scanning = 0
update_icon()
return
/obj/machinery/dnaforensics/attack_ai(mob/user as mob)
ui_interact(user)
/obj/machinery/dnaforensics/attack_hand(mob/user as mob)
ui_interact(user)
/obj/machinery/dnaforensics/verb/toggle_lid()
set category = "Object"
set name = "Toggle Lid"
set src in oview(1)
if(usr.stat || !isliving(usr))
return
if(scanning)
usr << "<span class='warning'>You can't do that while [src] is scanning!</span>"
return
closed = !closed
src.update_icon()
/obj/machinery/dnaforensics/update_icon()
..()
if(!(stat & NOPOWER) && scanning)
icon_state = "dnaworking"
else if(closed)
icon_state = "dnaclosed"
else
icon_state = "dnaopen"

View File

@@ -0,0 +1,108 @@
//microscope code itself
/obj/machinery/microscope
name = "high powered electron microscope"
desc = "A highly advanced microscope capable of zooming up to 3000x."
icon = 'icons/obj/forensics.dmi'
icon_state = "microscope"
anchored = 1
density = 1
var/obj/item/weapon/sample = null
var/report_num = 0
/obj/machinery/microscope/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(sample)
user << "<span class='warning'>There is already a slide in the microscope.</span>"
return
if(istype(W, /obj/item/weapon/forensics/slide) || istype(W, /obj/item/weapon/sample/print))
user << "<span class='notice'>You insert \the [W] into the microscope.</span>"
user.unEquip(W)
W.forceMove(src)
sample = W
update_icon()
return
/obj/machinery/microscope/attack_hand(mob/user)
if(!sample)
user << "<span class='warning'>The microscope has no sample to examine.</span>"
return
user << "<span class='notice'>The microscope whirrs as you examine \the [sample].</span>"
if(!do_after(user, 25) || !sample)
return
user << "<span class='notice'>Printing findings now...</span>"
var/obj/item/weapon/paper/report = new(get_turf(src))
report.stamped = list(/obj/item/weapon/stamp)
report.overlays = list("paper_stamped")
report_num++
if(istype(sample, /obj/item/weapon/forensics/slide))
var/obj/item/weapon/forensics/slide/slide = sample
if(slide.has_swab)
var/obj/item/weapon/forensics/swab/swab = slide.has_swab
report.name = "GSR report #[++report_num]: [swab.name]"
report.info = "<b>Scanned item:</b><br>[swab.name]<br><br>"
if(swab.gsr)
report.info += "Residue from a [swab.gsr] bullet detected."
else
report.info += "No gunpowder residue found."
else if(slide.has_sample)
var/obj/item/weapon/sample/fibers/fibers = slide.has_sample
report.name = "Fiber report #[++report_num]: [fibers.name]"
report.info = "<b>Scanned item:</b><br>[fibers.name]<br><br>"
if(fibers.evidence)
report.info = "Molecular analysis on provided sample has determined the presence of unique fiber strings.<br><br>"
for(var/fiber in fibers.evidence)
report.info += "<span class='notice'>Most likely match for fibers: [fiber]</span><br><br>"
else
report.info += "No fibers found."
else
report.name = "Empty slide report #[report_num]"
report.info = "Evidence suggests that there's nothing in this slide."
else if(istype(sample, /obj/item/weapon/sample/print))
report.name = "Fingerprint report #[report_num]: [sample.name]"
report.info = "<b>Fingerprint analysis report #[report_num]</b>: [sample.name]<br>"
var/obj/item/weapon/sample/print/card = sample
if(card.evidence && card.evidence.len)
report.info += "Surface analysis has determined unique fingerprint strings:<br><br>"
for(var/prints in card.evidence)
report.info += "<span class='notice'>Fingerprint string: </span>"
if(!is_complete_print(prints))
report.info += "INCOMPLETE PRINT"
else
report.info += "[prints]"
report.info += "<br>"
else
report.info += "No information available."
if(report)
report.update_icon()
if(report.info)
user << report.info
return
/obj/machinery/microscope/AltClick()
if(usr.lying || !Adjacent(usr) || !ishuman(usr))
return ..()
if(!sample)
usr << "<span class='warning'>\The [src] does not have a sample in it.</span>"
return
usr << "<span class='notice'>You remove \the [sample] from \the [src].</span>"
sample.forceMove(get_turf(src))
usr.put_in_hands(sample)
sample = null
update_icon()
return
/obj/machinery/microscope/update_icon()
icon_state = "microscope"
if(sample)
icon_state += "slide"

View File

@@ -0,0 +1,42 @@
/obj/item/weapon/forensics/slide
name = "microscope slide"
desc = "A pair of thin glass panes used in the examination of samples beneath a microscope."
icon_state = "slide"
var/obj/item/weapon/forensics/swab/has_swab
var/obj/item/weapon/sample/fibers/has_sample
/obj/item/weapon/forensics/slide/attackby(var/obj/item/weapon/W, var/mob/user)
if(has_swab || has_sample)
user << "<span class='warning'>There is already a sample in the slide.</span>"
return
if(istype (W, /obj/item/weapon/forensics/swab))
has_swab = W
else if(istype(W, /obj/item/weapon/sample/fibers))
has_sample = W
else
user << "<span class='warning'>You don't think this will fit.</span>"
return
user << "<span class='notice'>You insert the sample into the slide.</span>"
user.unEquip(W)
W.forceMove(src)
update_icon()
/obj/item/weapon/forensics/slide/attack_self(var/mob/user)
if(has_swab || has_sample)
user << "<span class='notice'>You remove \the sample from \the [src].</span>"
if(has_swab)
has_swab.loc = get_turf(src)
has_swab = null
if(has_sample)
has_sample.forceMove(get_turf(src))
has_sample = null
update_icon()
return
/obj/item/weapon/forensics/slide/update_icon()
if(!has_swab && !has_sample)
icon_state = "slide"
else if(has_swab)
icon_state = "slideswab"
else if(has_sample)
icon_state = "slidefiber"

View File

@@ -1,115 +0,0 @@
/obj/item/device/detective_scanner
name = "forensic scanner"
desc = "Used to scan objects for DNA and fingerprints."
icon_state = "forensic1"
var/list/stored = list()
w_class = 3.0
item_state = "electronic"
flags = CONDUCT | NOBLUDGEON
slot_flags = SLOT_BELT
/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.</span>"
user << "<span class='notice'>[M]'s Fingerprints: [md5(M.dna.uni_identity)]</span>"
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'>&nbsp;&nbsp;&nbsp;&nbsp;[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
/obj/item/device/detective_scanner/verb/wipe()
set name = "Wipe Forensic Data"
set category = "Object"
set src in view(1)
if (alert("Are you sure you want to wipe all data from [src]?",,"Yes","No") == "Yes")
stored = list()
usr << "<span class='notice'>Forensic data erase complete.</span>"

View File

@@ -1,313 +0,0 @@
/obj/machinery/computer/forensic_scanning
name = "high-res forensic scanning computer"
icon_keyboard = "security_key"
icon_screen = "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
//updating partial prints on other things
var/list/fprints = fresh.fields["fprints"]
if(fprints.len)
for(var/id in files)
var/datum/data/record/forensic/rec = files[id]
if(rec.update_prints(fprints))
files[id] = rec
/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]
found = rec.update_prints(card.fingerprints)
if (found)
files[id] = rec
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 += "<br><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)
if(..()) return 1
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 = sanitize(input("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 << "<span 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 = sanitize(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 << "<span 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()
qdel(I)
else
usr << "<span 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()

View File

@@ -0,0 +1,17 @@
//crime scene kit
/obj/item/weapon/storage/briefcase/crimekit
name = "crime scene kit"
desc = "A stainless steel-plated carrycase for all your forensic needs. Feels heavy."
icon = 'icons/obj/forensics.dmi'
icon_state = "case"
item_state = "case"
storage_slots = 14
/obj/item/weapon/storage/briefcase/crimekit/New()
..()
new /obj/item/weapon/storage/box/swabs(src)
new /obj/item/weapon/storage/box/fingerprints(src)
new /obj/item/weapon/reagent_containers/spray/luminol(src)
new /obj/item/device/uv_light(src)
new /obj/item/weapon/forensics/sample_kit(src)
new /obj/item/weapon/forensics/sample_kit/powder(src)

View File

@@ -96,56 +96,3 @@
/obj/item/weapon/evidencebag/examine(mob/user) /obj/item/weapon/evidencebag/examine(mob/user)
..(user) ..(user)
if (stored_item) user.examinate(stored_item) if (stored_item) user.examinate(stored_item)
/obj/item/weapon/storage/box/evidence
name = "evidence bag box"
desc = "A box claiming to contain evidence bags. Also holds fingerprint cards."
icon = 'icons/obj/storage.dmi'
icon_state = "box"
storage_slots= 14
max_w_class = 3
max_storage_space = 38
can_hold = list(/obj/item/weapon/f_card, /obj/item/weapon/evidencebag)
use_to_pickup = 1
New()
new /obj/item/weapon/evidencebag(src)
new /obj/item/weapon/evidencebag(src)
new /obj/item/weapon/evidencebag(src)
new /obj/item/weapon/evidencebag(src)
new /obj/item/weapon/evidencebag(src)
new /obj/item/weapon/evidencebag(src)
..()
return
/obj/item/weapon/f_card
name = "finger print card"
desc = "Used to take fingerprints."
icon = 'icons/obj/card.dmi'
icon_state = "fingerprint0"
var/amount = 10.0
item_state = "paper"
throwforce = 1
w_class = 1.0
slot_flags = SLOT_EARS
throw_speed = 3
throw_range = 5
/obj/item/weapon/f_card/add_fingerprint(mob/living/M as mob, ignoregloves = 0)
if(..())
var/mob/living/carbon/human/H = M
var/full_print = md5(H.dna.uni_identity)
fingerprints[full_print] = full_print
/obj/item/weapon/f_card/examine(mob/user)
..()
if(fingerprints.len)
user << "<span class='notice'>Fingerprints on this card:</span>"
for(var/print in fingerprints)
user << "<span class='notice'>\t[fingerprints[print]]</span>"
/obj/item/weapon/fcardholder
name = "fingerprint card case"
desc = "Apply finger print card."
icon = 'icons/obj/items.dmi'
icon_state = "fcardholder0"
item_state = "clipboard"

View File

@@ -0,0 +1,13 @@
/obj/item/weapon/reagent_containers/spray/luminol
name = "luminol bottle"
desc = "A bottle containing an odourless, colorless liquid."
icon = 'icons/obj/forensics.dmi'
icon_state = "luminol"
item_state = "cleaner"
amount_per_transfer_from_this = 10
possible_transfer_amounts = list(5,10)
volume = 250
/obj/item/weapon/reagent_containers/spray/luminol/New()
..()
reagents.add_reagent("luminol", 250)

View File

@@ -0,0 +1,158 @@
/obj/item/weapon/sample
name = "forensic sample"
icon = 'icons/obj/forensics.dmi'
w_class = 1
var/list/evidence = list()
/obj/item/weapon/sample/New(var/newloc, var/atom/supplied)
..(newloc)
if(supplied)
copy_evidence(supplied)
name = "[initial(name)] (\the [supplied])"
/obj/item/weapon/sample/print/New(var/newloc, var/atom/supplied)
..(newloc, supplied)
if(evidence && evidence.len)
icon_state = "fingerprint1"
/obj/item/weapon/sample/proc/copy_evidence(var/atom/supplied)
if(supplied.suit_fibers && supplied.suit_fibers.len)
evidence = supplied.suit_fibers.Copy()
supplied.suit_fibers.Cut()
/obj/item/weapon/sample/proc/merge_evidence(var/obj/item/weapon/sample/supplied, var/mob/user)
if(!supplied.evidence || !supplied.evidence.len)
return 0
evidence |= supplied.evidence
name = "[initial(name)] (combined)"
user << "<span class='notice'>You transfer the contents of \the [supplied] into \the [src].</span>"
return 1
/obj/item/weapon/sample/print/merge_evidence(var/obj/item/weapon/sample/supplied, var/mob/user)
if(!supplied.evidence || !supplied.evidence.len)
return 0
for(var/print in supplied.evidence)
if(evidence[print])
evidence[print] = stringmerge(evidence[print],supplied.evidence[print])
else
evidence[print] = supplied.evidence[print]
name = "[initial(name)] (combined)"
user << "<span class='notice'>You overlay \the [src] and \the [supplied], combining the print records.</span>"
return 1
/obj/item/weapon/sample/attackby(var/obj/O, var/mob/user)
if(O.type == src.type)
user.unEquip(O)
if(merge_evidence(O, user))
qdel(O)
return 1
return ..()
/obj/item/weapon/sample/fibers
name = "fiber bag"
desc = "Used to hold fiber evidence for the detective."
icon_state = "fiberbag"
/obj/item/weapon/sample/print
name = "fingerprint card"
desc = "Records a set of fingerprints."
icon = 'icons/obj/card.dmi'
icon_state = "fingerprint0"
item_state = "paper"
/obj/item/weapon/sample/print/attack_self(var/mob/user)
if(evidence && evidence.len)
return
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
if(H.gloves)
user << "<span class='warning'>Take \the [H.gloves] off first.</span>"
return
user << "<span class='notice'>You firmly press your fingertips onto the card.</span>"
var/fullprint = H.get_full_print()
evidence[fullprint] = fullprint
name = "[initial(name)] (\the [H])"
icon_state = "fingerprint1"
/obj/item/weapon/sample/print/attack(var/mob/living/M, var/mob/user)
if(!ishuman(M))
return ..()
if(evidence && evidence.len)
return 0
var/mob/living/carbon/human/H = M
if(H.gloves)
user << "<span class='warning'>\The [H] is wearing gloves.</span>"
return 1
if(user != H && H.a_intent != "help" && !H.lying)
user.visible_message("<span class='danger'>\The [user] tries to take prints from \the [H], but they move away.</span>")
return 1
if(user.zone_sel.selecting == "r_hand" || user.zone_sel.selecting == "l_hand")
var/has_hand
var/obj/item/organ/external/O = H.organs_by_name["r_hand"]
if(istype(O) && !O.is_stump())
has_hand = 1
else
O = H.organs_by_name["l_hand"]
if(istype(O) && !O.is_stump())
has_hand = 1
if(!has_hand)
user << "<span class='warning'>They don't have any hands.</span>"
return 1
user.visible_message("[user] takes a copy of \the [H]'s fingerprints.")
var/fullprint = H.get_full_print()
evidence[fullprint] = fullprint
copy_evidence(src)
name = "[initial(name)] (\the [H])"
icon_state = "fingerprint1"
return 1
return 0
/obj/item/weapon/sample/print/copy_evidence(var/atom/supplied)
if(supplied.fingerprints && supplied.fingerprints.len)
for(var/print in supplied.fingerprints)
evidence[print] = supplied.fingerprints[print]
supplied.fingerprints.Cut()
/obj/item/weapon/forensics/sample_kit
name = "fiber collection kit"
desc = "A magnifying glass and tweezers. Used to lift suit fibers."
icon_state = "m_glass"
w_class = 2
var/evidence_type = "fiber"
var/evidence_path = /obj/item/weapon/sample/fibers
/obj/item/weapon/forensics/sample_kit/proc/can_take_sample(var/mob/user, var/atom/supplied)
return (supplied.suit_fibers && supplied.suit_fibers.len)
/obj/item/weapon/forensics/sample_kit/proc/take_sample(var/mob/user, var/atom/supplied)
var/obj/item/weapon/sample/S = new evidence_path(get_turf(user), supplied)
user << "<span class='notice'>You transfer [S.evidence.len] [S.evidence.len > 1 ? "[evidence_type]s" : "[evidence_type]"] to \the [S].</span>"
/obj/item/weapon/forensics/sample_kit/afterattack(var/atom/A, var/mob/user, var/proximity)
if(!proximity)
return
add_fingerprint(user)
if(can_take_sample(user, A))
take_sample(user,A)
return 1
else
user << "<span class='warning'>You are unable to locate any [evidence_type]s on \the [A].</span>"
return ..()
/obj/item/weapon/forensics/sample_kit/powder
name = "fingerprint powder"
desc = "A jar containing aluminum powder and a specialized brush."
icon_state = "dust"
evidence_type = "fingerprint"
evidence_path = /obj/item/weapon/sample/print
/obj/item/weapon/forensics/sample_kit/powder/can_take_sample(var/mob/user, var/atom/supplied)
return (supplied.fingerprints && supplied.fingerprints.len)

View File

@@ -0,0 +1,44 @@
/obj/item/weapon/storage/box/swabs
name = "box of swab kits"
desc = "Sterilized equipment within. Do not contaminate."
icon = 'icons/obj/forensics.dmi'
icon_state = "dnakit"
can_hold = list(/obj/item/weapon/forensics/swab)
storage_slots = 14
/obj/item/weapon/storage/box/swabs/New()
..()
for(var/i=0;i<storage_slots,i++) // Fill 'er up.
new /obj/item/weapon/forensics/swab(src)
/obj/item/weapon/storage/box/slides
name = "microscope slide box"
icon_state = "solution_trays"
storage_slots = 7
/obj/item/weapon/storage/box/slides/New()
..()
for(var/i=0;i<storage_slots,i++)
new /obj/item/weapon/forensics/slide(src)
/obj/item/weapon/storage/box/evidence
name = "evidence bag box"
desc = "A box claiming to contain evidence bags."
storage_slots = 6
/obj/item/weapon/storage/box/evidence/New()
for(var/i=0;i<storage_slots,i++)
new /obj/item/weapon/evidencebag(src)
/obj/item/weapon/storage/box/fingerprints
name = "box of fingerprint cards"
desc = "Sterilized equipment within. Do not contaminate."
icon = 'icons/obj/forensics.dmi'
icon_state = "dnakit"
can_hold = list(/obj/item/weapon/sample/print)
storage_slots = 14
/obj/item/weapon/storage/box/fingerprints/New()
..()
for(var/i=0;i<storage_slots,i++)
new /obj/item/weapon/sample/print(src)

View File

@@ -0,0 +1,119 @@
/obj/item/weapon/forensics/swab
name = "swab kit"
desc = "A sterilized cotton swab and vial used to take forensic samples."
icon_state = "swab"
var/gsr = 0
var/list/dna
var/used
/obj/item/weapon/forensics/swab/proc/is_used()
return used
/obj/item/weapon/forensics/swab/attack(var/mob/living/M, var/mob/user)
if(!ishuman(M))
return ..()
if(is_used())
return 0
var/mob/living/carbon/human/H = M
var/sample_type
if(H.wear_mask)
user << "<span class='warning'>\The [H] is wearing a mask.</span>"
return 1
if(!H.dna || !H.dna.unique_enzymes)
user << "<span class='warning'>They don't seem to have DNA!</span>"
return 1
if(user != H && H.a_intent != "help" && !H.lying)
user.visible_message("<span class='danger'>\The [user] tries to take a swab sample from \the [H], but they move away.</span>")
return 1
if(user.zone_sel.selecting == "mouth")
if(!H.organs_by_name["head"])
user << "<span class='warning'>They don't have a head.</span>"
return 1
if(!H.check_has_mouth())
user << "<span class='warning'>They don't have a mouth.</span>"
return 1
user.visible_message("[user] swabs \the [H]'s mouth for a saliva sample.")
dna = list(H.dna.unique_enzymes)
sample_type = "DNA"
else if(user.zone_sel.selecting == "r_hand" || user.zone_sel.selecting == "l_hand")
var/has_hand
var/obj/item/organ/external/O = H.organs_by_name["r_hand"]
if(istype(O) && !O.is_stump())
has_hand = 1
else
O = H.organs_by_name["l_hand"]
if(istype(O) && !O.is_stump())
has_hand = 1
if(!has_hand)
user << "<span class='warning'>They don't have any hands.</span>"
return 1
user.visible_message("[user] swabs [H]'s palm for a sample.")
sample_type = "GSR"
gsr = H.gunshot_residue
else
return 0
if(sample_type)
used = 1
name = "[initial(name)] ([sample_type] - [H])"
desc = "[initial(desc)] The label on the vial reads 'Sample of [sample_type] from [H].'."
icon_state = "swab_used"
return 1
return 0
/obj/item/weapon/forensics/swab/afterattack(var/atom/A, var/mob/user, var/proximity)
if(!proximity || istype(A, /obj/item/weapon/forensics/slide))
return
if(is_used())
user << "<span class='warning'>This swab has already been used.</span>"
return
add_fingerprint(user)
var/list/choices = list()
if(A.blood_DNA)
choices |= "Blood"
if(istype(A, /obj/item/clothing))
choices |= "Gunshot Residue"
var/choice
if(!choices.len)
user << "<span class='warning'>There is no evidence on \the [A].</span>"
return
else if(choices.len == 1)
choice = choices[1]
else
choice = input("What kind of evidence are you looking for?","Evidence Collection") as null|anything in choices
if(!choice)
return
var/sample_type
if(choice == "Blood")
if(!A.blood_DNA || !A.blood_DNA.len) return
dna = A.blood_DNA.Copy()
sample_type = "blood"
else if(choice == "Gunshot Residue")
var/obj/item/clothing/B = A
if(!istype(B) || !B.gunshot_residue)
user << "<span class='warning'>There is no residue on \the [A].</span>"
return
gsr = B.gunshot_residue
sample_type = "residue"
if(sample_type)
user.visible_message("\The [user] swabs \the [A] for a sample.", "You swab \the [A] for a sample.")
name = "[initial(name)] ([sample_type] - [A])"
desc = "[initial(desc)] The label on the vial reads 'Sample of [sample_type] from [A].'."
icon_state = "swab_used"

View File

@@ -0,0 +1,69 @@
/obj/item/device/uv_light
name = "\improper UV light"
desc = "A small handheld black light."
icon_state = "uv_off"
slot_flags = SLOT_BELT
w_class = 2
item_state = "electronic"
matter = list(DEFAULT_WALL_MATERIAL = 150)
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
var/list/scanned = list()
var/list/stored_alpha = list()
var/list/reset_objects = list()
var/range = 3
var/on = 0
var/step_alpha = 50
/obj/item/device/uv_light/attack_self(var/mob/user)
on = !on
if(on)
set_light(range, 2, "#007fff")
processing_objects |= src
icon_state = "uv_on"
else
set_light(0)
clear_last_scan()
processing_objects -= src
icon_state = "uv_off"
/obj/item/device/uv_light/proc/clear_last_scan()
if(scanned.len)
for(var/atom/O in scanned)
O.invisibility = scanned[O]
if(O.fluorescent == 2) O.fluorescent = 1
scanned.Cut()
if(stored_alpha.len)
for(var/atom/O in stored_alpha)
O.alpha = stored_alpha[O]
if(O.fluorescent == 2) O.fluorescent = 1
stored_alpha.Cut()
if(reset_objects.len)
for(var/obj/item/I in reset_objects)
I.overlays -= I.blood_overlay
if(I.fluorescent == 2) I.fluorescent = 1
reset_objects.Cut()
/obj/item/device/uv_light/process()
clear_last_scan()
if(on)
step_alpha = round(255/range)
var/turf/origin = get_turf(src)
if(!origin)
return
for(var/turf/T in range(range, origin))
var/use_alpha = 255 - (step_alpha * get_dist(origin, T))
for(var/atom/A in T.contents)
if(A.fluorescent == 1)
A.fluorescent = 2 //To prevent light crosstalk.
if(A.invisibility)
scanned[A] = A.invisibility
A.invisibility = 0
stored_alpha[A] = A.alpha
A.alpha = use_alpha
if(istype(A, /obj/item))
var/obj/item/O = A
if(O.was_bloodied && !(O.blood_overlay in O.overlays))
O.overlays |= O.blood_overlay
reset_objects |= O

View File

@@ -318,9 +318,7 @@
H.germ_level = 0 H.germ_level = 0
update_icons() //apply the now updated overlays to the mob update_icons() //apply the now updated overlays to the mob
//Throwing stuff //Throwing stuff
/mob/proc/throw_item(atom/target) /mob/proc/throw_item(atom/target)
return return

View File

@@ -1018,8 +1018,14 @@
verbs += /mob/living/carbon/human/proc/bloody_doodle verbs += /mob/living/carbon/human/proc/bloody_doodle
return 1 //we applied blood to the item return 1 //we applied blood to the item
/mob/living/carbon/human/proc/get_full_print()
if(!dna ||!dna.uni_identity)
return
return md5(dna.uni_identity)
/mob/living/carbon/human/clean_blood(var/clean_feet) /mob/living/carbon/human/clean_blood(var/clean_feet)
.=..() .=..()
gunshot_residue = null
if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len) if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len)
feet_blood_color = null feet_blood_color = null
qdel(feet_blood_DNA) qdel(feet_blood_DNA)

View File

@@ -81,6 +81,7 @@
var/hand_blood_color var/hand_blood_color
var/list/flavor_texts = list() var/list/flavor_texts = list()
var/gunshot_residue
mob_bump_flag = HUMAN mob_bump_flag = HUMAN
mob_push_flags = ~HEAVY mob_push_flags = ~HEAVY

View File

@@ -316,4 +316,6 @@ proc/blood_splatter(var/target,var/datum/reagent/blood/source,var/large)
if(source.data["virus2"]) if(source.data["virus2"])
B.virus2 = virus_copylist(source.data["virus2"]) B.virus2 = virus_copylist(source.data["virus2"])
B.fluorescent = 0
B.invisibility = 0
return B return B

View File

@@ -7,6 +7,8 @@
slot_flags = SLOT_BELT | SLOT_EARS slot_flags = SLOT_BELT | SLOT_EARS
throwforce = 1 throwforce = 1
w_class = 1 w_class = 1
var/leaves_residue = 1
var/caliber = "" //Which kind of guns it can be loaded into var/caliber = "" //Which kind of guns it can be loaded into
var/projectile_type //The bullet type to create when New() is called var/projectile_type //The bullet type to create when New() is called
var/obj/item/projectile/BB = null //The loaded bullet - make it so that the projectiles are created only when needed? var/obj/item/projectile/BB = null //The loaded bullet - make it so that the projectiles are created only when needed?

View File

@@ -68,6 +68,16 @@
/obj/item/weapon/gun/projectile/proc/process_chambered() /obj/item/weapon/gun/projectile/proc/process_chambered()
if (!chambered) return if (!chambered) return
// Aurora forensics port, gunpowder residue.
if(chambered.leaves_residue)
var/mob/living/carbon/human/H = loc
if(istype(H))
if(!H.gloves)
H.gunshot_residue = chambered.caliber
else
var/obj/item/clothing/G = H.gloves
G.gunshot_residue = chambered.caliber
switch(handle_casings) switch(handle_casings)
if(EJECT_CASINGS) //eject casing onto ground. if(EJECT_CASINGS) //eject casing onto ground.
chambered.loc = get_turf(src) chambered.loc = get_turf(src)

View File

@@ -115,10 +115,3 @@
/datum/reagent/proc/reaction_mob(var/mob/target) /datum/reagent/proc/reaction_mob(var/mob/target)
touch_mob(target) touch_mob(target)
/datum/reagent/woodpulp
name = "Wood Pulp"
id = "woodpulp"
description = "A mass of wood fibers."
reagent_state = LIQUID
color = "#B97A57"

View File

@@ -375,19 +375,27 @@
/datum/reagent/sterilizine /datum/reagent/sterilizine
name = "Sterilizine" name = "Sterilizine"
id = "sterilizine" id = "sterilizine"
description = "Sterilizes wounds in preparation for surgery." description = "Sterilizes wounds in preparation for surgery and thoroughly removes blood."
reagent_state = LIQUID reagent_state = LIQUID
color = "#C8A5DC" color = "#C8A5DC"
touch_met = 5 touch_met = 5
/datum/reagent/sterilizine/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) /datum/reagent/sterilizine/affect_touch(var/mob/living/carbon/M, var/alien, var/removed)
M.germ_level -= min(removed*20, M.germ_level) M.germ_level -= min(removed*20, M.germ_level)
for(var/obj/item/I in M.contents)
I.was_bloodied = null
M.was_bloodied = null
/datum/reagent/sterilizine/touch_obj(var/obj/O) /datum/reagent/sterilizine/touch_obj(var/obj/O)
O.germ_level -= min(volume*20, O.germ_level) O.germ_level -= min(volume*20, O.germ_level)
O.was_bloodied = null
/datum/reagent/sterilizine/touch_turf(var/turf/T) /datum/reagent/sterilizine/touch_turf(var/turf/T)
T.germ_level -= min(volume*20, T.germ_level) T.germ_level -= min(volume*20, T.germ_level)
for(var/obj/item/I in T.contents)
I.was_bloodied = null
for(var/obj/effect/decal/cleanable/blood/B in T)
qdel(B)
/datum/reagent/leporazine /datum/reagent/leporazine
name = "Leporazine" name = "Leporazine"

View File

@@ -278,9 +278,6 @@
touch_met = 50 touch_met = 50
/datum/reagent/space_cleaner/touch_obj(var/obj/O) /datum/reagent/space_cleaner/touch_obj(var/obj/O)
if(istype(O, /obj/effect/decal/cleanable))
qdel(O)
else
O.clean_blood() O.clean_blood()
/datum/reagent/space_cleaner/touch_turf(var/turf/T) /datum/reagent/space_cleaner/touch_turf(var/turf/T)
@@ -382,3 +379,23 @@
id = "glue" id = "glue"
description = "An extremely powerful bonding agent." description = "An extremely powerful bonding agent."
color = "#FFFFCC" color = "#FFFFCC"
/datum/reagent/woodpulp
name = "Wood Pulp"
id = "woodpulp"
description = "A mass of wood fibers."
reagent_state = LIQUID
color = "#B97A57"
/datum/reagent/luminol
name = "Luminol"
id = "luminol"
description = "A compound that interacts with blood on the molecular level."
reagent_state = LIQUID
color = "#F2F3F4"
/datum/reagent/luminol/touch_obj(var/obj/O)
O.reveal_blood()
/datum/reagent/luminol/touch_mob(var/mob/living/L)
L.reveal_blood()

View File

@@ -2051,30 +2051,10 @@
required_reagents = list("space_up" = 1, "bluecuracao" = 1, "melonliquor" = 1) required_reagents = list("space_up" = 1, "bluecuracao" = 1, "melonliquor" = 1)
result_amount = 3 result_amount = 3
/* Removed xenoarcheology stuff /datum/chemical_reaction/luminol
datum name = "Luminol"
chemical_reaction id = "luminol"
lithiumsodiumtungstate //LiNa2WO4, not the easiest chem to mix result = "luminol"
name = "Lithium Sodium Tungstate" required_reagents = list("hydrogen" = 2, "carbon" = 2, "ammonia" = 2)
id = "lithiumsodiumtungstate" result_amount = 6
result = "lithiumsodiumtungstate"
required_reagents = list("lithium" = 1, "sodium" = 2, "tungsten" = 1, "oxygen" = 4)
result_amount = 8
density_separated_liquid
name = "Density separated sample"
id = "density_separated_sample"
result = "density_separated_sample"
secondary_results = list("chemical_waste" = 1)
required_reagents = list("ground_rock" = 1, "lithiumsodiumtungstate" = 2)
result_amount = 2
analysis_liquid
name = "Analysis sample"
id = "analysis_sample"
result = "analysis_sample"
secondary_results = list("chemical_waste" = 1)
required_reagents = list("density_separated_sample" = 5)
result_amount = 4
requires_heating = 1
*/

View File

@@ -107,6 +107,14 @@
..() ..()
reagents.add_reagent("cleaner", volume) reagents.add_reagent("cleaner", volume)
/obj/item/weapon/reagent_containers/spray/sterilizine
name = "sterilizine"
desc = "Great for hiding incriminating bloodstains and sterilizing scalpels."
/obj/item/weapon/reagent_containers/spray/sterilizine/New()
..()
reagents.add_reagent("sterilizine", volume)
/obj/item/weapon/reagent_containers/spray/pepper /obj/item/weapon/reagent_containers/spray/pepper
name = "pepperspray" name = "pepperspray"
desc = "Manufactured by UhangInc, used to blind and down an opponent quickly." desc = "Manufactured by UhangInc, used to blind and down an opponent quickly."

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 23 KiB

BIN
icons/obj/forensics.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,45 @@
<h3>Machine Status</h3>
<div class="item">
<div class="itemContent" style="width: 20%;">
{{:helper.link(data.scanning ? 'Halt Scan' : 'Begin Scan', 'signal-diag', {'scanItem' : 1}, null)}}
</div>
<div class="itemContent" style="width: 20%;">
{{:helper.link((data.lidstate ? 'Open Lid' : 'Close Lid'), (data.lidstate ? 'unlocked' : 'locked'), {'toggleLid' : 1}, null)}}
</div>
<div class="itemContent" style="width: 20%;">
{{:helper.link('Eject item', 'eject', {'ejectItem' : 1}, (data.bloodsamp && !data.scanning) ? null : 'disabled')}}
</div>
</div>
<h3>Scanner</h3>
<div class="item">
<div class="itemLabel" style="width: 21%;">Scan progress:</div>
<div class="itemContent" style="width: 35%;">
{{:helper.displayBar(data.scan_progress, 0, 100, 'good')}}
{{:data.scan_progress}} %
</div>
<div class="itemContent" style="width: 44%;">
{{if data.scan_progress >= 100}}
<span class="notice" style="width: 100%;">Scan completed successfully.</span>
{{/if}}
</div>
</div>
<div class="statusDisplay">
<div class="line">
<div class="statusLabel">Item:</div>
<div class="statusValue">
{{if data.bloodsamp}}
<span class="good">{{:data.bloodsamp}}</span>
{{else}}
<span class="bad">No item inserted</span>
{{/if}}
</div>
</div>
<div class="line">
<div class="statusLabel">Heuristic analysis:</div>
<div class="statusValue">
{{if data.bloodsamp_desc}}
<span class="average">{{:data.bloodsamp_desc}}</span>
{{/if}}
</div>
</div>
</div>

View File

@@ -1102,11 +1102,19 @@
#include "code\modules\clothing\under\jobs\security.dm" #include "code\modules\clothing\under\jobs\security.dm"
#include "code\modules\clothing\under\xenos\resomi.dm" #include "code\modules\clothing\under\xenos\resomi.dm"
#include "code\modules\customitems\item_spawning.dm" #include "code\modules\customitems\item_spawning.dm"
#include "code\modules\detectivework\evidence.dm" #include "code\modules\detectivework\footprints.dm"
#include "code\modules\detectivework\footprints_and_rag.dm"
#include "code\modules\detectivework\forensics.dm" #include "code\modules\detectivework\forensics.dm"
#include "code\modules\detectivework\scanner.dm" #include "code\modules\detectivework\microscope\dnascanner.dm"
#include "code\modules\detectivework\scanning_console.dm" #include "code\modules\detectivework\microscope\microscope.dm"
#include "code\modules\detectivework\microscope\slides.dm"
#include "code\modules\detectivework\tools\crimekit.dm"
#include "code\modules\detectivework\tools\evidencebag.dm"
#include "code\modules\detectivework\tools\luminol.dm"
#include "code\modules\detectivework\tools\rag.dm"
#include "code\modules\detectivework\tools\sample_kits.dm"
#include "code\modules\detectivework\tools\storage.dm"
#include "code\modules\detectivework\tools\swabs.dm"
#include "code\modules\detectivework\tools\uvlight.dm"
#include "code\modules\economy\Accounts.dm" #include "code\modules\economy\Accounts.dm"
#include "code\modules\economy\Accounts_DB.dm" #include "code\modules\economy\Accounts_DB.dm"
#include "code\modules\economy\ATM.dm" #include "code\modules\economy\ATM.dm"