mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
Porting forensics from Aurora.
This commit is contained in:
@@ -63,24 +63,24 @@
|
||||
#define LIFE_HUD 10 // STATUS_HUD that only reports dead or alive
|
||||
|
||||
//some colors
|
||||
#define COLOR_WHITE "#FFFFFF"
|
||||
#define COLOR_SILVER "#C0C0C0"
|
||||
#define COLOR_GRAY "#808080"
|
||||
#define COLOR_BLACK "#000000"
|
||||
#define COLOR_RED "#FF0000"
|
||||
#define COLOR_MAROON "#800000"
|
||||
#define COLOR_YELLOW "#FFFF00"
|
||||
#define COLOR_OLIVE "#808000"
|
||||
#define COLOR_LIME "#00FF00"
|
||||
#define COLOR_GREEN "#008000"
|
||||
#define COLOR_CYAN "#00FFFF"
|
||||
#define COLOR_TEAL "#008080"
|
||||
#define COLOR_BLUE "#0000FF"
|
||||
#define COLOR_NAVY "#000080"
|
||||
#define COLOR_PINK "#FF00FF"
|
||||
#define COLOR_PURPLE "#800080"
|
||||
#define COLOR_ORANGE "#FF9900"
|
||||
|
||||
#define COLOR_WHITE "#FFFFFF"
|
||||
#define COLOR_SILVER "#C0C0C0"
|
||||
#define COLOR_GRAY "#808080"
|
||||
#define COLOR_BLACK "#000000"
|
||||
#define COLOR_RED "#FF0000"
|
||||
#define COLOR_MAROON "#800000"
|
||||
#define COLOR_YELLOW "#FFFF00"
|
||||
#define COLOR_OLIVE "#808000"
|
||||
#define COLOR_LIME "#00FF00"
|
||||
#define COLOR_GREEN "#008000"
|
||||
#define COLOR_CYAN "#00FFFF"
|
||||
#define COLOR_TEAL "#008080"
|
||||
#define COLOR_BLUE "#0000FF"
|
||||
#define COLOR_NAVY "#000080"
|
||||
#define COLOR_PINK "#FF00FF"
|
||||
#define COLOR_PURPLE "#800080"
|
||||
#define COLOR_ORANGE "#FF9900"
|
||||
#define COLOR_LUMINOL "#66FFFF"
|
||||
// Shuttles.
|
||||
|
||||
// These define the time taken for the shuttle to get to the space station, and the time before it leaves again.
|
||||
|
||||
@@ -43,6 +43,20 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
||||
group = "Security"
|
||||
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
|
||||
name = "Kitchen supply crate"
|
||||
contains = list(/obj/item/weapon/reagent_containers/food/condiment/flour,
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
var/list/fingerprintshidden
|
||||
var/fingerprintslast = null
|
||||
var/list/blood_DNA
|
||||
var/was_bloodied
|
||||
var/blood_color
|
||||
var/last_bumped = 0
|
||||
var/pass_flags = 0
|
||||
var/throwpass = 0
|
||||
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/fluorescent // Shows up under a UV light.
|
||||
|
||||
///Chemistry.
|
||||
var/datum/reagents/reagents = null
|
||||
@@ -23,6 +25,9 @@
|
||||
//Detective Work, used for the duplicate data points kept in the scanners
|
||||
var/list/original_atom
|
||||
|
||||
/atom/proc/reveal_blood()
|
||||
return
|
||||
|
||||
/atom/proc/assume_air(datum/gas_mixture/giver)
|
||||
return null
|
||||
|
||||
@@ -304,7 +309,7 @@ its easier to just keep the beam vertical.
|
||||
fingerprints = list()
|
||||
|
||||
//Hash this shit.
|
||||
var/full_print = md5(H.dna.uni_identity)
|
||||
var/full_print = H.get_full_print()
|
||||
|
||||
// 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.
|
||||
blood_DNA = list()
|
||||
|
||||
was_bloodied = 1
|
||||
blood_color = "#A10808"
|
||||
if(istype(M))
|
||||
if (!istype(M.dna, /datum/dna))
|
||||
@@ -406,10 +412,10 @@ its easier to just keep the beam vertical.
|
||||
if(toxvomit)
|
||||
this.icon_state = "vomittox_[pick(1,4)]"
|
||||
|
||||
|
||||
/atom/proc/clean_blood()
|
||||
if(!simulated)
|
||||
return
|
||||
fluorescent = 0
|
||||
src.germ_level = 0
|
||||
if(istype(blood_DNA, /list))
|
||||
blood_DNA = null
|
||||
|
||||
@@ -111,12 +111,11 @@
|
||||
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?
|
||||
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
|
||||
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")
|
||||
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
|
||||
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)
|
||||
|
||||
@@ -15,6 +15,4 @@
|
||||
return src.attack_hand(user)
|
||||
|
||||
/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)
|
||||
@@ -24,22 +24,6 @@
|
||||
user << "Error, no route to host."
|
||||
|
||||
/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)
|
||||
|
||||
/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user)
|
||||
|
||||
@@ -750,7 +750,7 @@ About the new airlock wires panel:
|
||||
if(src.isElectrified())
|
||||
if(src.shock(user, 75))
|
||||
return
|
||||
if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll))
|
||||
if(istype(C, /obj/item/taperoll))
|
||||
return
|
||||
|
||||
src.add_fingerprint(user)
|
||||
|
||||
@@ -202,8 +202,6 @@
|
||||
..()
|
||||
|
||||
/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)
|
||||
|
||||
if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name())
|
||||
|
||||
@@ -72,8 +72,6 @@
|
||||
// src.sd_SetLuminosity(0)
|
||||
|
||||
/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))
|
||||
add_fingerprint(user)
|
||||
src.disable = !src.disable
|
||||
|
||||
@@ -22,6 +22,19 @@ var/global/list/image/splatter_cache=list()
|
||||
var/amount = 5
|
||||
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()
|
||||
for(var/datum/disease/D in viruses)
|
||||
D.cure(0)
|
||||
|
||||
@@ -29,6 +29,13 @@ var/global/list/image/fluidtrack_cache=list()
|
||||
src.basecolor=_color
|
||||
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...
|
||||
/obj/effect/decal/cleanable/blood/tracks
|
||||
amount = 0
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/obj/effect/decal/cleanable
|
||||
var/list/random_icon_states = list()
|
||||
|
||||
/obj/effect/decal/cleanable/clean_blood()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/decal/cleanable/New()
|
||||
if (random_icon_states && length(src.random_icon_states) > 0)
|
||||
src.icon_state = pick(src.random_icon_states)
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
//It should be used purely for appearance. For gameplay effects caused by items covering body parts, use body_parts_covered.
|
||||
var/flags_inv = 0
|
||||
var/body_parts_covered = 0 //see setup.dm for appropriate bit flags
|
||||
|
||||
|
||||
var/item_flags = 0 //Miscellaneous flags pertaining to equippable objects.
|
||||
|
||||
|
||||
//var/heat_transfer_coefficient = 1 //0 prevents all transfers, 1 is invisible
|
||||
var/gas_transfer_coefficient = 1 // for leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets)
|
||||
var/permeability_coefficient = 1 // for chemicals/diseases
|
||||
@@ -494,6 +494,12 @@ var/list/global/slot_flags_enumeration = list(
|
||||
var/obj/item/clothing/gloves/G = src
|
||||
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)
|
||||
if (!..())
|
||||
@@ -608,10 +614,10 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
|
||||
usr.visible_message("[zoomdevicename ? "[usr] looks up from the [src.name]" : "[usr] lowers the [src.name]"].")
|
||||
|
||||
return
|
||||
|
||||
|
||||
/obj/item/proc/pwr_drain()
|
||||
return 0 // Process Kill
|
||||
|
||||
/obj/item/proc/resolve_attackby(atom/A, mob/source)
|
||||
return A.attackby(src,source)
|
||||
|
||||
|
||||
|
||||
@@ -281,7 +281,6 @@
|
||||
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/alt(src)
|
||||
new /obj/item/device/detective_scanner(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)
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to
|
||||
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()
|
||||
..()
|
||||
if(istype(loc, /area/chapel))
|
||||
|
||||
@@ -216,3 +216,6 @@ var/const/enterloopsanity = 100
|
||||
if(A.density && !(A.flags & ON_BORDER))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/turf/proc/update_blood_overlays()
|
||||
return
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "clothing"
|
||||
siemens_coefficient = 0.9
|
||||
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.
|
||||
@@ -15,6 +16,11 @@
|
||||
/obj/item/clothing/proc/update_clothing_icon()
|
||||
return
|
||||
|
||||
// Aurora forensics port.
|
||||
/obj/item/clothing/clean_blood()
|
||||
..()
|
||||
gunshot_residue = null
|
||||
|
||||
//BS12: Species-restricted clothing check.
|
||||
/obj/item/clothing/mob_can_equip(M as mob, slot)
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
item_state = "det_suit"
|
||||
blood_overlay_type = "coat"
|
||||
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)
|
||||
|
||||
/obj/item/clothing/suit/storage/det_trench/grey
|
||||
@@ -109,7 +109,7 @@
|
||||
desc = "A forensics technician jacket."
|
||||
item_state = "det_suit"
|
||||
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)
|
||||
|
||||
/obj/item/clothing/suit/storage/forensics/red
|
||||
|
||||
0
code/modules/detectivework/footprints.dm
Normal file
0
code/modules/detectivework/footprints.dm
Normal 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
|
||||
//the print must be there for it to be complete. (Prints are 32 digits)
|
||||
var/const/FINGERPRINT_COMPLETE = 6
|
||||
|
||||
153
code/modules/detectivework/microscope/dnascanner.dm
Normal file
153
code/modules/detectivework/microscope/dnascanner.dm
Normal 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"
|
||||
108
code/modules/detectivework/microscope/microscope.dm
Normal file
108
code/modules/detectivework/microscope/microscope.dm
Normal 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"
|
||||
42
code/modules/detectivework/microscope/slides.dm
Normal file
42
code/modules/detectivework/microscope/slides.dm
Normal 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"
|
||||
@@ -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'> [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>"
|
||||
@@ -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()
|
||||
17
code/modules/detectivework/tools/crimekit.dm
Normal file
17
code/modules/detectivework/tools/crimekit.dm
Normal 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)
|
||||
@@ -96,56 +96,3 @@
|
||||
/obj/item/weapon/evidencebag/examine(mob/user)
|
||||
..(user)
|
||||
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"
|
||||
13
code/modules/detectivework/tools/luminol.dm
Normal file
13
code/modules/detectivework/tools/luminol.dm
Normal 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)
|
||||
@@ -71,7 +71,7 @@
|
||||
icon_state = "raglit"
|
||||
else
|
||||
icon_state = "rag"
|
||||
|
||||
|
||||
var/obj/item/weapon/reagent_containers/food/drinks/bottle/B = loc
|
||||
if(istype(B))
|
||||
B.update_icon()
|
||||
@@ -83,7 +83,7 @@
|
||||
if(reagents.total_volume)
|
||||
var/target_text = trans_dest? "\the [trans_dest]" : "\the [user.loc]"
|
||||
user.visible_message("<span class='danger'>\The [user] begins to wring out [src] over [target_text].</span>", "<span class='notice'>You begin to wring out [src] over [target_text].</span>")
|
||||
|
||||
|
||||
if(do_after(user, reagents.total_volume*5)) //50 for a fully soaked rag
|
||||
if(trans_dest)
|
||||
reagents.trans_to(trans_dest, reagents.total_volume)
|
||||
@@ -114,29 +114,29 @@
|
||||
if(user.zone_sel.selecting == "mouth")
|
||||
user.do_attack_animation(src)
|
||||
user.visible_message(
|
||||
"<span class='danger'>\The [user] smothers [target] with [src]!</span>",
|
||||
"<span class='warning'>You smother [target] with [src]!</span>",
|
||||
"<span class='danger'>\The [user] smothers [target] with [src]!</span>",
|
||||
"<span class='warning'>You smother [target] with [src]!</span>",
|
||||
"You hear some struggling and muffled cries of surprise"
|
||||
)
|
||||
|
||||
|
||||
//it's inhaled, so... maybe CHEM_BLOOD doesn't make a whole lot of sense but it's the best we can do for now
|
||||
reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_BLOOD)
|
||||
update_name()
|
||||
else
|
||||
wipe_down(target, user)
|
||||
return
|
||||
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/glass/rag/afterattack(atom/A as obj|turf|area, mob/user as mob, proximity)
|
||||
if(!proximity)
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(istype(A, /obj/structure/reagent_dispensers))
|
||||
if(!reagents.get_free_space())
|
||||
user << "<span class='warning'>\The [src] is already soaked.</span>"
|
||||
return
|
||||
|
||||
|
||||
if(A.reagents && A.reagents.trans_to_obj(src, reagents.maximum_volume))
|
||||
user.visible_message("<span class='notice'>\The [user] soaks [src] using [A].</span>", "<span class='notice'>You soak [src] using [A].</span>")
|
||||
update_name()
|
||||
@@ -167,7 +167,7 @@
|
||||
return
|
||||
if(!can_ignite())
|
||||
return
|
||||
|
||||
|
||||
//also copied from matches
|
||||
if(reagents.get_reagent_amount("phoron")) // the phoron explodes when exposed to fire
|
||||
visible_message("<span class='danger'>\The [src] conflagrates violently!</span>")
|
||||
@@ -176,7 +176,7 @@
|
||||
e.start()
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
|
||||
processing_objects += src
|
||||
set_light(2, null, "#E38F46")
|
||||
on_fire = 1
|
||||
@@ -187,7 +187,7 @@
|
||||
processing_objects -= src
|
||||
set_light(0)
|
||||
on_fire = 0
|
||||
|
||||
|
||||
//rags sitting around with 1 second of burn time left is dumb.
|
||||
//ensures players always have a few seconds of burn time left when they light their rag
|
||||
if(burn_time <= 5)
|
||||
@@ -209,7 +209,7 @@
|
||||
var/turf/location = get_turf(src)
|
||||
if(location)
|
||||
location.hotspot_expose(700, 5)
|
||||
|
||||
|
||||
if(burn_time <= 0)
|
||||
processing_objects -= src
|
||||
new /obj/effect/decal/cleanable/ash(location)
|
||||
158
code/modules/detectivework/tools/sample_kits.dm
Normal file
158
code/modules/detectivework/tools/sample_kits.dm
Normal 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)
|
||||
44
code/modules/detectivework/tools/storage.dm
Normal file
44
code/modules/detectivework/tools/storage.dm
Normal 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)
|
||||
119
code/modules/detectivework/tools/swabs.dm
Normal file
119
code/modules/detectivework/tools/swabs.dm
Normal 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"
|
||||
|
||||
69
code/modules/detectivework/tools/uvlight.dm
Normal file
69
code/modules/detectivework/tools/uvlight.dm
Normal 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
|
||||
@@ -318,9 +318,7 @@
|
||||
H.germ_level = 0
|
||||
update_icons() //apply the now updated overlays to the mob
|
||||
|
||||
|
||||
//Throwing stuff
|
||||
|
||||
/mob/proc/throw_item(atom/target)
|
||||
return
|
||||
|
||||
|
||||
@@ -1018,8 +1018,14 @@
|
||||
verbs += /mob/living/carbon/human/proc/bloody_doodle
|
||||
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)
|
||||
.=..()
|
||||
gunshot_residue = null
|
||||
if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len)
|
||||
feet_blood_color = null
|
||||
qdel(feet_blood_DNA)
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
var/hand_blood_color
|
||||
|
||||
var/list/flavor_texts = list()
|
||||
var/gunshot_residue
|
||||
|
||||
mob_bump_flag = HUMAN
|
||||
mob_push_flags = ~HEAVY
|
||||
|
||||
@@ -316,4 +316,6 @@ proc/blood_splatter(var/target,var/datum/reagent/blood/source,var/large)
|
||||
if(source.data["virus2"])
|
||||
B.virus2 = virus_copylist(source.data["virus2"])
|
||||
|
||||
B.fluorescent = 0
|
||||
B.invisibility = 0
|
||||
return B
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
slot_flags = SLOT_BELT | SLOT_EARS
|
||||
throwforce = 1
|
||||
w_class = 1
|
||||
|
||||
var/leaves_residue = 1
|
||||
var/caliber = "" //Which kind of guns it can be loaded into
|
||||
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?
|
||||
|
||||
@@ -68,6 +68,16 @@
|
||||
/obj/item/weapon/gun/projectile/proc/process_chambered()
|
||||
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)
|
||||
if(EJECT_CASINGS) //eject casing onto ground.
|
||||
chambered.loc = get_turf(src)
|
||||
|
||||
@@ -115,10 +115,3 @@
|
||||
|
||||
/datum/reagent/proc/reaction_mob(var/mob/target)
|
||||
touch_mob(target)
|
||||
|
||||
/datum/reagent/woodpulp
|
||||
name = "Wood Pulp"
|
||||
id = "woodpulp"
|
||||
description = "A mass of wood fibers."
|
||||
reagent_state = LIQUID
|
||||
color = "#B97A57"
|
||||
|
||||
@@ -375,19 +375,27 @@
|
||||
/datum/reagent/sterilizine
|
||||
name = "Sterilizine"
|
||||
id = "sterilizine"
|
||||
description = "Sterilizes wounds in preparation for surgery."
|
||||
description = "Sterilizes wounds in preparation for surgery and thoroughly removes blood."
|
||||
reagent_state = LIQUID
|
||||
color = "#C8A5DC"
|
||||
touch_met = 5
|
||||
|
||||
/datum/reagent/sterilizine/affect_touch(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
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)
|
||||
O.germ_level -= min(volume*20, O.germ_level)
|
||||
O.was_bloodied = null
|
||||
|
||||
/datum/reagent/sterilizine/touch_turf(var/turf/T)
|
||||
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
|
||||
name = "Leporazine"
|
||||
|
||||
@@ -278,10 +278,7 @@
|
||||
touch_met = 50
|
||||
|
||||
/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)
|
||||
if(volume >= 1)
|
||||
@@ -382,3 +379,23 @@
|
||||
id = "glue"
|
||||
description = "An extremely powerful bonding agent."
|
||||
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()
|
||||
|
||||
@@ -2051,30 +2051,10 @@
|
||||
required_reagents = list("space_up" = 1, "bluecuracao" = 1, "melonliquor" = 1)
|
||||
result_amount = 3
|
||||
|
||||
/* Removed xenoarcheology stuff
|
||||
datum
|
||||
chemical_reaction
|
||||
lithiumsodiumtungstate //LiNa2WO4, not the easiest chem to mix
|
||||
name = "Lithium Sodium Tungstate"
|
||||
id = "lithiumsodiumtungstate"
|
||||
result = "lithiumsodiumtungstate"
|
||||
required_reagents = list("lithium" = 1, "sodium" = 2, "tungsten" = 1, "oxygen" = 4)
|
||||
result_amount = 8
|
||||
/datum/chemical_reaction/luminol
|
||||
name = "Luminol"
|
||||
id = "luminol"
|
||||
result = "luminol"
|
||||
required_reagents = list("hydrogen" = 2, "carbon" = 2, "ammonia" = 2)
|
||||
result_amount = 6
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
/obj/item/weapon/reagent_containers/spray/proc/Spray_at(atom/A as mob|obj, mob/user as mob, proximity)
|
||||
if (A.density && proximity)
|
||||
A.visible_message("[usr] sprays [A] with [src].")
|
||||
A.visible_message("[usr] sprays [A] with [src].")
|
||||
reagents.splash(A, amount_per_transfer_from_this)
|
||||
else
|
||||
spawn(0)
|
||||
@@ -107,6 +107,14 @@
|
||||
..()
|
||||
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
|
||||
name = "pepperspray"
|
||||
desc = "Manufactured by UhangInc, used to blind and down an opponent quickly."
|
||||
@@ -161,12 +169,12 @@
|
||||
volume = 600
|
||||
origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3, TECH_ENGINEERING = 3)
|
||||
|
||||
/obj/item/weapon/reagent_containers/spray/chemsprayer/Spray_at(atom/A as mob|obj)
|
||||
/obj/item/weapon/reagent_containers/spray/chemsprayer/Spray_at(atom/A as mob|obj)
|
||||
var/direction = get_dir(src, A)
|
||||
var/turf/T = get_turf(A)
|
||||
var/turf/T1 = get_step(T,turn(direction, 90))
|
||||
var/turf/T2 = get_step(T,turn(direction, -90))
|
||||
var/list/the_targets = list(T, T1, T2)
|
||||
var/turf/T2 = get_step(T,turn(direction, -90))
|
||||
var/list/the_targets = list(T, T1, T2)
|
||||
|
||||
for(var/a = 1 to 3)
|
||||
spawn(0)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 23 KiB |
BIN
icons/obj/forensics.dmi
Normal file
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
45
nano/templates/dnaforensics.tmpl
Normal file
45
nano/templates/dnaforensics.tmpl
Normal 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>
|
||||
16
polaris.dme
16
polaris.dme
@@ -1102,11 +1102,19 @@
|
||||
#include "code\modules\clothing\under\jobs\security.dm"
|
||||
#include "code\modules\clothing\under\xenos\resomi.dm"
|
||||
#include "code\modules\customitems\item_spawning.dm"
|
||||
#include "code\modules\detectivework\evidence.dm"
|
||||
#include "code\modules\detectivework\footprints_and_rag.dm"
|
||||
#include "code\modules\detectivework\footprints.dm"
|
||||
#include "code\modules\detectivework\forensics.dm"
|
||||
#include "code\modules\detectivework\scanner.dm"
|
||||
#include "code\modules\detectivework\scanning_console.dm"
|
||||
#include "code\modules\detectivework\microscope\dnascanner.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_DB.dm"
|
||||
#include "code\modules\economy\ATM.dm"
|
||||
|
||||
Reference in New Issue
Block a user