diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 5e005c5f..7c80ad3a 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -14,6 +14,7 @@ var/datum/global_hud/global_hud = new()
var/obj/screen/blurry
var/list/vimpaired
var/list/darkMask
+ var/obj/screen/lum
var/obj/screen/nvg
/datum/global_hud/New()
@@ -38,6 +39,13 @@ var/datum/global_hud/global_hud = new()
nvg.layer = 17
nvg.mouse_opacity = 0
+ lum = new /obj/screen()
+ lum.screen_loc = "1,1"
+ lum.icon = 'icons/obj/nvg_hud_full.dmi'
+ lum.icon_state = "luminol_hud"
+ lum.layer = 17
+ lum.mouse_opacity = 0
+
var/obj/screen/O
var/i
//that nasty looking dither you get when you're short-sighted
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 7c72634a..fe39ae58 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -43,6 +43,18 @@ 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/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 = 15
+ 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/snacks/flour,
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index d63ba30c..ca6760c7 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -6,6 +6,7 @@
var/list/fingerprintshidden
var/fingerprintslast = null
var/list/blood_DNA
+ var/list/stored_DNA = list()
var/blood_color
var/last_bumped = 0
var/pass_flags = 0
@@ -417,7 +418,6 @@ its easier to just keep the beam vertical.
if(toxvomit)
this.icon_state = "vomittox_[pick(1,4)]"
-
/atom/proc/clean_blood()
src.germ_level = 0
if(istype(blood_DNA, /list))
@@ -442,4 +442,4 @@ its easier to just keep the beam vertical.
return 0
/atom/proc/checkpass(passflag)
- return pass_flags&passflag
+ return pass_flags&passflag
\ No newline at end of file
diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm
index 12b69144..b6c51689 100644
--- a/code/game/jobs/job/security.dm
+++ b/code/game/jobs/job/security.dm
@@ -95,11 +95,9 @@
spawn_positions = 1
supervisors = "the head of security"
selection_color = "#ffeeee"
- alt_titles = list("Forensic Technician")
access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court)
minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court)
- alt_titles = list("Forensic Technician")
minimal_player_age = 3
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -108,28 +106,64 @@
if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(H), slot_back)
if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel_norm(H), slot_back)
if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back)
- H.equip_to_slot_or_del(new /obj/item/clothing/under/det(H), slot_w_uniform)
- H.equip_to_slot_or_del(new /obj/item/clothing/shoes/brown(H), slot_shoes)
+ H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/navy/det(H), slot_w_uniform)
+ H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/device/pda/detective(H), slot_belt)
/* var/obj/item/clothing/mask/cigarette/CIG = new /obj/item/clothing/mask/cigarette(H)
CIG.light("")
H.equip_to_slot_or_del(CIG, slot_wear_mask) */
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves)
- 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)
- else
- H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_suit(H), slot_wear_suit)
- H.equip_to_slot_or_del(new /obj/item/clothing/head/det_hat(H), slot_head)
+ H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_suit(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/weapon/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/survival(H), slot_r_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
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H.back), 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)
+
+ return 1
+
+/datum/job/forensics
+ title = "Forensic Technician"
+ flag = FORENSICS
+ department_flag = ENGSEC
+ faction = "Station"
+ total_positions = 1
+ spawn_positions = 1
+ supervisors = "the head of security"
+ selection_color = "#ffeeee"
+
+ access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court)
+ minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court)
+ alt_titles = list("Crime Scene Investigator")
+ minimal_player_age = 3
+ equip(var/mob/living/carbon/human/H)
+ if(!H) return 0
+ H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_sec(H), slot_l_ear)
+ switch(H.backbag)
+ if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(H), slot_back)
+ if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel_norm(H), slot_back)
+ if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back)
+ H.equip_to_slot_or_del(new /obj/item/clothing/under/det/slob(H), slot_w_uniform)
+ H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes)
+ H.equip_to_slot_or_del(new /obj/item/device/pda/detective(H), slot_belt)
+/* var/obj/item/clothing/mask/cigarette/CIG = new /obj/item/clothing/mask/cigarette(H)
+ CIG.light("")
+ H.equip_to_slot_or_del(CIG, slot_wear_mask) */
+ H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves)
+ if(H.mind.role_alt_title && H.mind.role_alt_title == "Crime Scene Investigator")
+ H.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/det_suit(H), slot_wear_suit)
+ else
+ H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/labcoat(H), slot_wear_suit)
+
+ 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/survival(H), slot_r_hand)
+ H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_l_hand)
+ else
+ H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H.back), slot_in_backpack)
+ H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_in_backpack)
return 1
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 8b7ff491..1547ce82 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -13,6 +13,7 @@ var/const/AI =(1<<8)
var/const/CYBORG =(1<<9)
var/const/INTERN_SEC =(1<<10)
var/const/INTERN_ENG =(1<<11)
+var/const/FORENSICS =(1<<12)
var/const/MEDSCI =(1<<1)
@@ -113,6 +114,7 @@ var/list/security_positions = list(
"Head of Security",
"Warden",
"Detective",
+ "Forensic Technician",
"Security Officer",
"Security Cadet"
)
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index 48ade625..bc52a6ad 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -43,6 +43,9 @@
for(var/atom/A in contents)
A.clean_blood()
+ for(var/obj/item/clothing/C in contents)
+ C.gsr = 0
+
for(var/obj/item/I in contents)
I.decontaminate()
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index 9d1b0c94..574597e0 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -50,6 +50,8 @@ var/global/list/image/splatter_cache=list()
return
if(amount < 1)
return
+ if(invisibility != 0)
+ return
if(perp.shoes)//Adding blood to shoes
perp.shoes.blood_color = basecolor
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 808640a4..1b256543 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -4,6 +4,7 @@
var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
var/abstract = 0
var/item_state = null
+ var/wasbloody = 0
var/r_speed = 1.0
var/health = null
var/burn_point = null
@@ -103,7 +104,7 @@
size = "huge"
else
//if ((CLUMSY in usr.mutations) && prob(50)) t = "funny-looking"
- usr << "This is a [src.blood_DNA ? "bloody " : ""]\icon[src][src.name]. It is a [size] item."
+ usr << "This is a [src.blood_DNA ? "bloody " : ""][src.wasbloody == 2 ? "luminol covered " : ""]\icon[src][src.name]. It is a [size] item."
if(src.desc)
usr << src.desc
return
@@ -552,9 +553,21 @@
. = ..()
if(blood_overlay)
overlays.Remove(blood_overlay)
+ blood_overlay = null
if(istype(src, /obj/item/clothing/gloves))
var/obj/item/clothing/gloves/G = src
G.transfer_blood = 0
+ if(wasbloody == 2)
+ wasbloody = 1
+ return
+
+/obj/item/proc/reveal_blood()
+ if(wasbloody == 1)
+ wasbloody = 2
+ generate_blood_overlay()
+ blood_overlay.color = "#007fff"
+ /*blood_overlay.invisibility = INVISIBILITY_LUMINOL*/
+ overlays += blood_overlay
/obj/item/add_blood(mob/living/carbon/human/M as mob)
@@ -578,6 +591,8 @@
if(blood_DNA[M.dna.unique_enzymes])
return 0 //already bloodied with this blood. Cannot add more.
blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
+ stored_DNA[M.dna.unique_enzymes] = M.dna.b_type
+ wasbloody = 1
return 1 //we applied blood to the item
/obj/item/proc/generate_blood_overlay()
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index e0e1e3dc..58da2b2a 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -232,6 +232,31 @@
new /obj/item/device/encryptionkey/headset_med(src)
return
+/obj/structure/closet/secure_closet/forensics
+ name = "Forensic tech's closet"
+ req_access = list(access_forensics_lockers)
+ icon_state = "sec1"
+ icon_closed = "sec"
+ icon_locked = "sec1"
+ icon_opened = "secopen"
+ icon_broken = "secbroken"
+ icon_off = "secoff"
+
+ New()
+ ..()
+ sleep(2)
+ new /obj/item/clothing/under/rank/navy/det/forensics(src)
+ new /obj/item/clothing/suit/storage/forensics/blue(src)
+ new /obj/item/clothing/suit/storage/forensics/red(src)
+ new /obj/item/clothing/gloves/black(src)
+ new /obj/item/clothing/shoes/brown(src)
+ new /obj/item/weapon/storage/box/evidence(src)
+ new /obj/item/device/radio/headset/headset_sec(src)
+ new /obj/item/clothing/suit/armor/det_suit(src)
+ new /obj/item/taperoll/police(src)
+ new /obj/item/clothing/tie/holobadge(src)
+ return
+
/obj/structure/closet/secure_closet/detective
name = "Detective's Cabinet"
@@ -248,24 +273,17 @@
sleep(2)
new /obj/item/clothing/under/det(src)
new /obj/item/clothing/under/det/black(src)
- new /obj/item/clothing/under/det/slob(src)
new /obj/item/clothing/suit/storage/det_suit(src)
new /obj/item/clothing/suit/storage/det_suit/black(src)
- new /obj/item/clothing/suit/storage/forensics/blue(src)
- new /obj/item/clothing/suit/storage/forensics/red(src)
new /obj/item/clothing/gloves/black(src)
new /obj/item/clothing/head/det_hat(src)
new /obj/item/clothing/head/det_hat/black(src)
new /obj/item/clothing/shoes/brown(src)
new /obj/item/weapon/storage/box/evidence(src)
new /obj/item/device/radio/headset/headset_sec(src)
- new /obj/item/device/detective_scanner(src)
new /obj/item/clothing/suit/armor/det_suit(src)
- new /obj/item/ammo_magazine/c45r(src)
- new /obj/item/ammo_magazine/c45r(src)
new /obj/item/taperoll/police(src)
- new /obj/item/weapon/gun/projectile/detective/semiauto(src)
- new /obj/item/clothing/tie/holster/armpit(src)
+ new /obj/item/clothing/tie/holobadge(src)
return
/obj/structure/closet/secure_closet/detective/update_icon()
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index ed5bf1d2..440c8556 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -344,6 +344,14 @@
M.clean_blood()
if(ishuman(M))
M:update_inv_gloves()
+ if(ishuman(M))
+ var/mob/living/carbon/human/B = M
+ if(B.gloves)
+ var/obj/item/clothing/gloves/C = B.gloves
+ C.gsr = 0
+ else
+ B.gsr = 0
+
for(var/mob/V in viewers(src, null))
V.show_message("\blue [M] washes their hands using \the [src].")
@@ -392,6 +400,11 @@
if(user.get_active_hand() != I) return //Person has switched hands or the item in their hands
O.clean_blood()
+
+ if(istype(O, /obj/item/clothing))
+ var/obj/item/clothing/G = O
+ G.gsr = 0
+
user.visible_message( \
"\blue [user] washes \a [I] using \the [src].", \
"\blue You wash \a [I] using \the [src].")
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 9dddf399..d96fc888 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -1,6 +1,7 @@
/obj/item/clothing
name = "clothing"
var/list/species_restricted = null //Only these species can wear this kit.
+ var/gsr = 0
//BS12: Species-restricted clothing check.
/obj/item/clothing/mob_can_equip(M as mob, slot)
diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm
index 351befea..328b7b58 100644
--- a/code/modules/clothing/under/jobs/security.dm
+++ b/code/modules/clothing/under/jobs/security.dm
@@ -225,6 +225,13 @@
item_state = "wardentanclothes"
item_color = "wardentanclothes"
+/obj/item/clothing/under/rank/navy/det
+ name = "Investigator's uniform"
+ desc = "A smart pair of khakis and a dress shirt."
+ icon_state = "wardentanclothes"
+ item_state = "wardentanclothes"
+ item_color = "wardentanclothes"
+
/obj/item/clothing/under/rank/navy/officer
name = "Naval Officer Uniform"
desc = "A service unfirom worn by an officer of the NanoTrasen Naval branch."
@@ -247,4 +254,11 @@
desc = "A tan service uniform worn by a security officer."
icon_state = "officertanclothes"
item_state = "officertanclothes"
+ item_color = "officertanclothes"
+
+/obj/item/clothing/under/rank/navy/det/forensics
+ name = "Technician's uniform"
+ desc = "A tan service uniform worn by a forensics officer."
+ icon_state = "officertanclothes"
+ item_state = "officertanclothes"
item_color = "officertanclothes"
\ No newline at end of file
diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm
index 7148a5af..c4120d5e 100644
--- a/code/modules/detectivework/evidence.dm
+++ b/code/modules/detectivework/evidence.dm
@@ -96,6 +96,7 @@
icon = 'icons/obj/card.dmi'
icon_state = "fingerprint0"
var/amount = 10.0
+ var/complete_prints = list()
item_state = "paper"
throwforce = 1
w_class = 1.0
@@ -108,4 +109,4 @@
desc = "Apply finger print card."
icon = 'icons/obj/items.dmi'
icon_state = "fcardholder0"
- item_state = "clipboard"
+ item_state = "clipboard"
\ No newline at end of file
diff --git a/code/modules/detectivework/forensics.dm b/code/modules/detectivework/forensics.dm
new file mode 100644
index 00000000..5488587c
--- /dev/null
+++ b/code/modules/detectivework/forensics.dm
@@ -0,0 +1,666 @@
+//swabs and code therein
+
+/obj/item/weapon/storage/box/swabs
+ name = "box of swab kits"
+ desc = "Sterilized equipment within. Do not contaminate."
+ icon = 'forensics.dmi'
+ icon_state = "dnakit"
+ storage_slots=14
+ can_hold = list("/obj/item/weapon/forensics/swab")
+ max_combined_w_class = 42
+
+ New()
+ ..()
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+ new /obj/item/weapon/forensics/swab( src )
+
+/obj/item/weapon/forensics/swab
+ name = "swab kit"
+ desc = "A sterilized cotton swab and vial used to take forensic samples."
+ icon = 'forensics.dmi'
+ icon_state = "swab"
+ flags = FPRINT | TABLEPASS | CONDUCT | NOBLUDGEON
+ w_class = 1.0
+ var/used = 0
+ var/gsr = 0
+ var/list/dna = list()
+
+/obj/item/weapon/forensics/swab/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen))
+ var/tmp_label = sanitize(input(user, "Enter a label for [src.name]","Label"))
+ if(length(tmp_label) > 20)
+ user << "\red The label can be at most 20 characters long."
+ else
+ user << "\blue You set the label to \"[tmp_label]\"."
+ name = "[initial(name)] \"[tmp_label]\"."
+
+/obj/item/weapon/forensics/swab/attack(mob/living/carbon/human/M as mob, mob/user as mob)
+ if(used == 1)
+ user << "\blue This swab has already been used."
+ return
+ else if(user.zone_sel.selecting == "mouth")
+ user.visible_message("[user] swabs [M]'s mouth for a saliva sample.", "You swab [M]'s mouth for a saliva sample.")
+ dna = M.dna.unique_enzymes
+ used = 1
+ name = "swab of [M]'s DNA"
+ desc = "[initial(desc)]
\blue The label on the vial reads 'Sample of [M]'s DNA'."
+ update_icon()
+ return
+ else if(user.zone_sel.selecting == "r_hand" || user.zone_sel.selecting == "l_hand")
+ user.visible_message("[user] swabs [M]'s palm for a sample.", "You swab [M]'s palm for a sample.")
+ gsr = M.gsr
+ used = 1
+ name = "swab of [M] for GSR tests"
+ desc = "[initial(desc)]
\blue The label on the vial reads 'GSR Sample from [M]'."
+ update_icon()
+ return
+ else
+ return
+
+/obj/item/weapon/forensics/swab/afterattack(atom/A as obj, mob/user as mob, proximity)
+ if(istype(A, /obj/item/weapon/forensics/slide))
+ return
+ if(used == 1)
+ return
+ if(!proximity) return
+ if(loc != user)
+ return
+
+ add_fingerprint(user)
+
+ if (istype(A, /obj/effect/decal/cleanable/blood) || istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable/blood/gibs))
+ if(!isnull(A.blood_DNA))
+ for(var/blood in A.blood_DNA)
+ dna = A.blood_DNA
+ user.visible_message("[user] swabs [A] for a sample.", "You swab [A] for a DNA sample.")
+ used = 1
+ update_icon()
+ return
+
+ else if(istype(A, /obj/item/clothing))
+ switch(alert(user,"What would you like to swab for?",,"Blood","Gunshot Residue","Cancel"))
+ if("Blood")
+ dna = A.stored_DNA
+ user.visible_message("[user] swabs [A] for a sample.", "You swab [A] for a DNA sample.")
+ used = 1
+ name = "swab of DNA from [A]"
+ desc = "[initial(desc)]
\blue The label on the vial reads 'Sample of DNA from [A].'."
+ update_icon()
+ return
+ if("Gunshot Residue")
+ var/obj/item/clothing/B = A
+ gsr = B.gsr
+ user.visible_message("[user] swabs [A] for a sample.", "You swab [A] for a GSR sample.")
+ used = 1
+ name = "swab of [A] for GSR tests"
+ desc = "[initial(desc)]
\blue The label on the vial reads 'GSR Sample from [A].'"
+ update_icon()
+ return
+ if("Cancel")
+ return
+
+ else if (istype(A, /obj/item/))
+ if(!isnull(A.stored_DNA))
+ dna = A.stored_DNA
+ user.visible_message("[user] swabs [A] for a sample.", "You swab [A] for a DNA sample.")
+ used = 1
+ desc = "[initial(desc)]
\blue The label on the vial reads 'Sample of DNA from [A].'."
+ update_icon()
+ return
+
+
+
+/obj/item/weapon/forensics/swab/update_icon()
+ if(used == 0)
+ icon_state = "swab"
+ if(used == 1)
+ icon_state = "swab_used"
+
+//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 = 'forensics.dmi'
+ icon_state = "case"
+ item_state = "case"
+ storage_slots=14
+ max_combined_w_class = 42
+
+//microscope slide code and such
+/obj/item/weapon/storage/box/slides
+ name = "microscope slide box"
+ icon_state = "solution_trays"
+
+ New()
+ ..()
+ new /obj/item/weapon/forensics/slide( src )
+ new /obj/item/weapon/forensics/slide( src )
+ new /obj/item/weapon/forensics/slide( src )
+ new /obj/item/weapon/forensics/slide( src )
+ new /obj/item/weapon/forensics/slide( src )
+ new /obj/item/weapon/forensics/slide( src )
+ new /obj/item/weapon/forensics/slide( src )
+
+/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 = 'forensics.dmi'
+ icon_state = "slide"
+ flags = FPRINT | TABLEPASS | CONDUCT | NOBLUDGEON
+ w_class = 1.0
+ var/slidesamp = 0
+ var/gsr = 0
+ var/list/fibers_complete = list()
+
+/obj/item/weapon/forensics/slide/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(slidesamp == 1)
+ user << "\red There is already a sample in the slide."
+ return
+ if(istype (W, /obj/item/weapon/forensics/swab))
+ var/obj/item/weapon/forensics/swab/D = W
+ user << "\blue You insert the sample into the slide."
+ slidesamp = 1
+ user.drop_item()
+ D.loc = src
+ gsr = D.gsr
+ icon_state = "slideswab"
+ update_icon()
+ else if(istype (W, /obj/item/weapon/fiberbag))
+ var/obj/item/weapon/fiberbag/E = W
+ user << "\blue You insert the sample into the slide."
+ slidesamp = 1
+ user.drop_item()
+ E.loc = src
+ fibers_complete = E.fibers_complete
+ icon_state = "slidefiber"
+ update_icon()
+ else
+ user << "\red You don't think this will fit."
+ return
+
+/obj/item/weapon/forensics/slide/verb/verb_remove_from_slide()
+ set category = "Object"
+ set name = "Empty Slide"
+ set src in usr
+
+ if(issilicon(usr))
+ return
+
+ var/obj/item/weapon/W = locate() in src
+ if(W)
+ usr << "You remove \the [W] from \the [src]."
+ src.slidesamp = 0
+ src.gsr = 0
+ src.fibers_complete = list()
+ src.update_icon()
+ W.loc = get_turf(src)
+ return
+
+/obj/item/weapon/forensics/slide/update_icon()
+ if(slidesamp == 0)
+ icon_state = "slide"
+
+//microscope code itself
+/obj/machinery/microscope
+ name = "high powered electron microscope"
+ desc = "A highly advanced microscope capable of zooming up to 3000x."
+ icon = 'forensics.dmi'
+ icon_state = "microscope"
+ anchored = 1
+ var/obj/item/weapon/forensics/sample = null
+ var/report_num = 0
+ density = 1
+
+/obj/machinery/microscope/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(sample)
+ user << "\red There is already a slide in the microscope."
+ return
+ else if(istype(W, /obj/item/weapon/forensics/slide))
+ user << "\blue You insert the slide into the microscope."
+ sample = W
+ user.drop_item()
+ W.loc = src
+ update_icon()
+ return
+ else if(istype(W, /obj/item/weapon/f_card))
+ user << "\blue You insert the fingerprint card into the microscope."
+ sample = W
+ user.drop_item()
+ W.loc = src
+ update_icon()
+ return
+
+/obj/machinery/microscope/attack_hand(mob/user)
+ var/obj/item/weapon/W = locate() in src
+ if(sample)
+ switch(alert("What would you like to do?",,"Examine for GSR","Examine Fingerprints","Examine Fibers","Cancel"))
+ if("Examine for GSR")
+ if(istype(W, /obj/item/weapon/forensics/slide))
+ user << "\blue The microscope whirrs as you examine the sample."
+ spawn(25)
+ var/obj/item/weapon/forensics/slide/C = W
+ if(C.gsr == 0)
+ user << "\red No traces of gunshot residue were found."
+ else
+ user << "\blue Printing findings now..."
+ var/obj/item/weapon/paper/P = new(src)
+ P.name = "GSR report #[++report_num]: [C.name]"
+ P.stamped = list(/obj/item/weapon/stamp)
+ P.overlays = list("paper_stamped")
+
+ var/data = "No information available."
+ if(C.gsr != null)
+ data = "Molecular analysis on provided sample has determined the presence of chemicals composing gunpowder.
"
+ data += "GSR Analysis: Positive
Significant amount of residue detected.
"
+ else
+ data += "No residue found.
"
+ P.info = "GSR analysis report #[report_num]
"
+ P.info += "Scanned item:
[C.name]
" + data
+ P.loc = src.loc
+ P.update_icon()
+ return
+ else
+ user << "\red I don't think that is applicable here..."
+ return
+
+ if("Examine Fingerprints")
+ if(istype(W, /obj/item/weapon/f_card))
+ user << "\blue The microscope whirrs as you examine the sample."
+ spawn(25)
+ user << "\blue Printing findings now..."
+ var/obj/item/weapon/paper/Y = new(src)
+ Y.name = "Fingerprint report #[++report_num]: [W.name]"
+ Y.stamped = list(/obj/item/weapon/stamp)
+ Y.overlays = list("paper_stamped")
+
+ var/data = "No information available."
+ var/obj/item/weapon/f_card/B = W
+ if(B.complete_prints != null)
+ data = "Surface analysis on provided card has determined the presence of uique fingerprint strings.
"
+ for(var/prints in B.complete_prints)
+ data += "\blue Fingerprint string: [B.complete_prints[prints]]
"
+ else
+ data += "No fingerprints found.
"
+ Y.info = "Fingerprint analysis report #[report_num]
"
+ Y.info += "Scanned item:
[B.name]
" + data
+ Y.loc = src.loc
+ Y.update_icon()
+ return
+ else
+ user << "\red I don't think that is applicable here..."
+ return
+
+ if("Examine Fibers")
+ if(istype(W, /obj/item/weapon/forensics/slide))
+ user << "\blue The microscope whirrs as you examine the sample."
+ spawn(25)
+ user << "\blue Printing findings now..."
+ var/obj/item/weapon/paper/Z = new(src)
+ Z.name = "Fiber report #[++report_num]: [W.name]"
+ Z.stamped = list(/obj/item/weapon/stamp)
+ Z.overlays = list("paper_stamped")
+
+ var/data = "No information available."
+ var/obj/item/weapon/fiberbag/D = W
+ if(D.fibers_complete != null)
+ data = "Molecular analysis on provided sample has determined the presence of unique fiber strings.
"
+ for(var/j = 1, j <= D.fibers_complete.len, j++)
+ data += "\blue Most likely match for fibers: [D.fibers_complete[j]]
"
+ else
+ data += "No fibers found.
"
+ Z.info = "Fiber analysis report #[report_num]
"
+ Z.info += "Scanned item:
[D.name]
" + data
+ Z.loc = src.loc
+ Z.update_icon()
+ return
+ else
+ user << "\red I don't think that is applicable here..."
+ return
+ if("Cancel")
+ return
+ else
+ user << "\blue The microscope has no sample."
+
+/obj/machinery/microscope/verb/verb_remove_slide()
+ set category = "Object"
+ set name = "Remove Sample"
+ set src in oview(1)
+
+ if(issilicon(usr))
+ return
+
+ var/obj/item/weapon/W = locate() in src
+ if(W)
+ usr << "You remove \the [W] from \the [src]."
+ src.sample = null
+ src.update_icon()
+ W.loc = get_turf(src)
+ return
+ else
+ usr << "This microscope does not have a sample in it."
+
+/obj/machinery/microscope/update_icon()
+ if(sample)
+ icon_state = "microscopeslide"
+ else
+ icon_state = "microscope"
+
+//fingerprint powder
+/obj/item/weapon/forensics/powder
+ name = "fingerprint powder"
+ desc = "A jar containing aluminum powder and a specialized brush."
+ icon = 'forensics.dmi'
+ icon_state = "dust"
+ var/list/complete_prints = list()
+ var/stored = list()
+ var/data_enty = list()
+ w_class = 1.0
+
+/obj/item/weapon/forensics/powder/afterattack(atom/A as obj|turf|area, mob/user as mob, proximity)
+ if(!proximity) return
+ if(loc != user)
+ return
+
+ add_fingerprint(user)
+
+ if ((!A.fingerprints || !A.fingerprints.len))
+ user.visible_message("\blue [user] carefully begins to print [A] using their [src].","\blue Unable to locate any fingerprints on [A]!")
+ return 0
+
+ if(!A.fingerprints || !A.fingerprints.len)
+ if(A.fingerprints)
+ del(A.fingerprints)
+ else
+ user << "\blue You managed to isolate [A.fingerprints.len] fingerprints and transfer them to an adhesive sheet."
+ /* for(var/i in A.fingerprints)
+ var/print = A.fingerprints[i]
+ var/obj/item/weapon/f_card/F = new /obj/item/weapon/f_card( user.loc )
+ F.complete_prints += print
+ F.name = "[initial(name)] from ([A])"
+ F.icon_state = "fingerprint1" */
+ //We keep this, because it's the start of the string.
+ var/list/data_entry = stored
+ if(islist(data_entry)) //Yay, it was already stored!
+ //Merge the fingerprints.
+ complete_prints = data_entry//[1] Starting the main part of the hack: I'm removing the number references from the code, hopefully I can use a singular sum_list commabnd as a result.
+ for(var/print in A.fingerprints)
+ var/merged_print = complete_prints[print]
+ if(!merged_print)
+ complete_prints[print] = A.fingerprints[print]
+ else
+ complete_prints[print] = stringmerge(complete_prints[print],A.fingerprints[print])
+ //We nix EVERYTHING that that has to do with non-print bullshite. So, yah, shorter code.
+ var/list/sum_list//[4] Pack it back up! || Except not 4 anymore, just one
+ sum_list/*[1]*/ = A.fingerprints ? A.fingerprints.Copy() : null
+ //nixed all of the sum_list commands that didn't do shit.
+ stored = sum_list
+ var/obj/item/weapon/f_card/F = new /obj/item/weapon/f_card( user.loc )
+ F.complete_prints = stored
+ stored = list()
+ data_enty = list()
+ complete_prints = list()
+ F.name = "fingerprint card from ([A])"
+ F.icon_state = "fingerprint1"
+ return 0
+
+//fiber collection
+/obj/item/weapon/fiberbag
+ name = "fiber bag"
+ desc = "Used to hold fiber evidence for the detective."
+ icon = 'icons/obj/forensics.dmi'
+ icon_state = "fiberbag"
+ var/list/fibers_complete = list()
+
+/obj/item/weapon/forensics/fiberkit
+ name = "Fiber Collection Kit"
+ desc = "A magnifying glass and tweezers. Used to lift suit fibers."
+ icon = 'forensics.dmi'
+ icon_state = "m_glass"
+ var/list/fibers_complete = list()
+ var/stored = list()
+ var/data_enty = list()
+
+/obj/item/weapon/forensics/fiberkit/afterattack(atom/A as obj|turf|area, mob/user as mob, proximity)
+ if(!proximity) return
+ if(loc != user)
+ return
+
+ add_fingerprint(user)
+
+ if((!A.suit_fibers))
+ user.visible_message("\blue [user] carefully begins to examine [A] using their [src].","\blue Unable to locate any suit fibers on [A]!")
+ return 0
+ else
+ user << "\blue You managed to isolate [A.suit_fibers.len] samples of fiber evidence and transfer it to an evidence bag."
+ var/list/data_entry = stored
+ if(islist(data_entry)) //Yay, it was already stored!
+ //Do the stuff that does the stuff
+ fibers_complete = data_entry//[2] Again, not needed because single entry lists and not multiple entry cascades.
+ for(var/j = 1, j <= A.suit_fibers.len, j++) //Fibers~~~
+ if(!fibers_complete.Find(A.suit_fibers[j])) //It isn't! Add!
+ fibers_complete += A.suit_fibers[j]
+ var/list/sum_list//[4] Pack it back up! || Except not 4 anymore, just one
+ sum_list/*[1]*/ = A.suit_fibers ? A.suit_fibers.Copy() : null
+ //nixed all of the sum_list commands that didn't do shit.
+ stored = sum_list
+ var/obj/item/weapon/fiberbag/F = new /obj/item/weapon/fiberbag( user.loc )
+ F.fibers_complete = stored
+ stored = list()
+ data_enty = list()
+ fibers_complete = list()
+ F.name = "fiber bag from ([A])"
+ return 0
+
+
+
+//Luminol and Goggles
+
+/obj/item/clothing/glasses/UV
+ name = "Ultraviolet Goggles"
+ desc = "A pair of specialized forensic glasses designed to detect luminol flouresence.."
+ icon_state = "thermal"
+ item_state = "glasses"
+ origin_tech = "magnets=3"
+
+/obj/item/weapon/reagent_containers/spray/luminol
+ name = "luminol bottle"
+ desc = "A bottle containing an ordorless, 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)
+
+//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
+ var/ui_title = "QuikScan DNA Analyzer"
+ var/range = 10
+ density = 1
+
+
+ //vars important for analyzing
+ var/obj/item/weapon/forensics/swab/bloodsamp = null
+ var/closed = 0
+ var/scanning = 0
+ var/scanner_progress = 0
+ var/scanner_rate = 2.50 //scanning takes 40 seconds
+ var/last_process_worldtime = 0
+ var/report_num = 0
+
+/obj/machinery/dnaforensics/attackby(obj/item/weapon/forensics/swab/W as obj, mob/user as mob)
+ if(src.bloodsamp)
+ user << "\red There is already a sample in the machine."
+ return
+ if(src.closed == 1)
+ user << "\red Open the cover before inserting the sample."
+ return
+ if(W.used == 1)
+ src.bloodsamp = W
+ user.drop_item()
+ W.loc = src
+ user << "You insert [W] into the machine."
+
+/obj/machinery/dnaforensics/update_icon()
+ ..()
+ if(! (stat & (NOPOWER)) && (scanning) && (closed == 1))
+ icon_state = "dnaworking"
+ else if(! (stat & (NOPOWER)) && (scanning != 1) && (closed == 1))
+ icon_state = "dnaclosed"
+ else if(! (stat & (NOPOWER)) && (scanning != 1) && (closed == 0))
+ icon_state = "dnaopen"
+
+/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
+
+ // this is the data which will be sent to the ui
+ var/data[0]
+ 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.") : "")
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
+ if (!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "dnaforensics.tmpl", "QuikScan DNA Analyzer", 540, 326)
+ // when the ui is first opened this is the data it will use
+ ui.set_initial_data(data)
+ // open the new ui window
+ ui.open()
+ // auto update every Master Controller tick
+ ui.set_auto_update(1)
+
+/obj/machinery/dnaforensics/Topic(href, href_list)
+ if(stat & (NOPOWER))
+ return 0 // don't update UIs attached to this object
+
+ src.add_fingerprint(usr)
+
+ if(href_list["scanItem"])
+ if(scanning)
+ stop_scanning()
+ else
+ if(bloodsamp)
+ if(closed == 1)
+ scanner_progress = 0
+ scanning = 1
+ usr << "Scan initiated."
+ update_icon()
+ else
+ usr << "Please close sample lid before initiating scan."
+ else
+ usr << "Insert an item to scan."
+
+ if(href_list["ejectItem"])
+ if(bloodsamp)
+ bloodsamp.loc = src.loc
+ bloodsamp = null
+
+ return 1
+
+/obj/machinery/dnaforensics/process()
+ if(scanning)
+ if(!bloodsamp || bloodsamp.loc != src)
+ bloodsamp = null
+ stop_scanning()
+ 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/stop_scanning()
+ scanning = 0
+
+/obj/machinery/dnaforensics/proc/complete_scan()
+ src.visible_message("\blue \icon[src] makes an insistent chime.", 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.
"
+ for(var/blood in bloodsamp.dna)
+ data += "\blue Blood type: [bloodsamp.dna[blood]]
\nDNA: [blood]
"
+ else
+ data += "No DNA found.
"
+ P.info = "[src] analysis report #[report_num]
"
+ P.info += "Scanned item:
[bloodsamp.name]
[bloodsamp.desc]
" + data
+ P.loc = src.loc
+ P.update_icon()
+ stop_scanning()
+ update_icon()
+ return
+
+/obj/machinery/dnaforensics/attack_ai(mob/user as mob)
+ return src.attack_hand(user)
+
+/obj/machinery/dnaforensics/attack_paw(mob/user as mob)
+ return src.attack_hand(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(issilicon(usr))
+ return
+
+ if(scanning)
+ usr << "You can't do that while [src] is scanning!"
+ return
+
+ if(closed == 1)
+ icon_state = "dnaopen"
+ closed = 0
+ src.update_icon()
+ else
+ icon_state = "dnaclosed"
+ closed = 1
+ src.update_icon()
+
+/obj/machinery/printer
+ name = "printer"
+ desc = "Your standard printer."
+ icon = 'icons/obj/forensics.dmi'
+ icon_state = "printer"
+ var/printing = 0
+ density = 1
+ anchored = 1
\ No newline at end of file
diff --git a/code/modules/economy/Job_Departments.dm b/code/modules/economy/Job_Departments.dm
index 06e2b58a..b1e10f12 100644
--- a/code/modules/economy/Job_Departments.dm
+++ b/code/modules/economy/Job_Departments.dm
@@ -67,6 +67,8 @@ var/list/station_departments = list("Command", "Medical", "Engineering", "Scienc
/datum/job/detective/department = "Security"
+/datum/job/forensics/department = "Security"
+
/datum/job/officer/department = "Security"
/datum/job/intern_sec/department = "Security"
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 69af9ce2..ffe888f0 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -72,6 +72,8 @@
if(w_uniform.blood_DNA)
msg += "[t_He] [t_is] wearing \icon[w_uniform] [w_uniform.gender==PLURAL?"some":"a"] blood-stained [w_uniform.name][tie_msg]!\n"
+ else if(w_uniform.wasbloody == 2)
+ msg += "[t_He] [t_is] wearing \icon[w_uniform] [w_uniform.gender==PLURAL?"some":"a"] luminol-covered [w_uniform.name][tie_msg]!\n"
else
msg += "[t_He] [t_is] wearing \icon[w_uniform] \a [w_uniform][tie_msg].\n"
@@ -79,6 +81,8 @@
if(head)
if(head.blood_DNA)
msg += "[t_He] [t_is] wearing \icon[head] [head.gender==PLURAL?"some":"a"] blood-stained [head.name] on [t_his] head!\n"
+ else if(head.wasbloody == 2)
+ msg += "[t_He] [t_is] wearing \icon[head] [head.gender==PLURAL?"some":"a"] luminol-covered [head.name] on [t_his] head!\n"
else
msg += "[t_He] [t_is] wearing \icon[head] \a [head] on [t_his] head.\n"
@@ -86,6 +90,8 @@
if(wear_suit)
if(wear_suit.blood_DNA)
msg += "[t_He] [t_is] wearing \icon[wear_suit] [wear_suit.gender==PLURAL?"some":"a"] blood-stained [wear_suit.name]!\n"
+ else if(wear_suit.wasbloody == 2)
+ msg += "[t_He] [t_is] wearing \icon[wear_suit] [wear_suit.gender==PLURAL?"some":"a"] luminol-covered [wear_suit.name]!\n"
else
msg += "[t_He] [t_is] wearing \icon[wear_suit] \a [wear_suit].\n"
@@ -93,6 +99,8 @@
if(s_store && !skipsuitstorage)
if(s_store.blood_DNA)
msg += "[t_He] [t_is] carrying \icon[s_store] [s_store.gender==PLURAL?"some":"a"] blood-stained [s_store.name] on [t_his] [wear_suit.name]!\n"
+ else if(s_store.wasbloody == 2)
+ msg += "[t_He] [t_is] carrying \icon[s_store] [s_store.gender==PLURAL?"some":"a"] luminol-covered [s_store.name] on [t_his] [wear_suit.name]!\n"
else
msg += "[t_He] [t_is] carrying \icon[s_store] \a [s_store] on [t_his] [wear_suit.name].\n"
@@ -100,6 +108,8 @@
if(back)
if(back.blood_DNA)
msg += "[t_He] [t_has] \icon[back] [back.gender==PLURAL?"some":"a"] blood-stained [back] on [t_his] back.\n"
+ else if(back.wasbloody == 2)
+ msg += "[t_He] [t_has] \icon[back] [back.gender==PLURAL?"some":"a"] luminol-covered [back] on [t_his] back.\n"
else
msg += "[t_He] [t_has] \icon[back] \a [back] on [t_his] back.\n"
@@ -107,6 +117,8 @@
if(l_hand)
if(l_hand.blood_DNA)
msg += "[t_He] [t_is] holding \icon[l_hand] [l_hand.gender==PLURAL?"some":"a"] blood-stained [l_hand.name] in [t_his] left hand!\n"
+ else if(l_hand.wasbloody == 2)
+ msg += "[t_He] [t_is] holding \icon[l_hand] [l_hand.gender==PLURAL?"some":"a"] luminol-covered [l_hand.name] in [t_his] left hand!\n"
else
msg += "[t_He] [t_is] holding \icon[l_hand] \a [l_hand] in [t_his] left hand.\n"
@@ -114,6 +126,8 @@
if(r_hand)
if(r_hand.blood_DNA)
msg += "[t_He] [t_is] holding \icon[r_hand] [r_hand.gender==PLURAL?"some":"a"] blood-stained [r_hand.name] in [t_his] right hand!\n"
+ else if(r_hand.wasbloody == 2)
+ msg += "[t_He] [t_is] holding \icon[r_hand] [r_hand.gender==PLURAL?"some":"a"] luminol-covered [r_hand.name] in [t_his] right hand!\n"
else
msg += "[t_He] [t_is] holding \icon[r_hand] \a [r_hand] in [t_his] right hand.\n"
@@ -121,6 +135,8 @@
if(gloves && !skipgloves)
if(gloves.blood_DNA)
msg += "[t_He] [t_has] \icon[gloves] [gloves.gender==PLURAL?"some":"a"] blood-stained [gloves.name] on [t_his] hands!\n"
+ else if(gloves.wasbloody == 2)
+ msg += "[t_He] [t_has] \icon[gloves] [gloves.gender==PLURAL?"some":"a"] luminol-covered [gloves.name] on [t_his] hands!\n"
else
msg += "[t_He] [t_has] \icon[gloves] \a [gloves] on [t_his] hands.\n"
else if(blood_DNA)
@@ -139,6 +155,8 @@
if(belt)
if(belt.blood_DNA)
msg += "[t_He] [t_has] \icon[belt] [belt.gender==PLURAL?"some":"a"] blood-stained [belt.name] about [t_his] waist!\n"
+ else if(belt.wasbloody == 2)
+ msg += "[t_He] [t_has] \icon[belt] [belt.gender==PLURAL?"some":"a"] luminol-covered [belt.name] about [t_his] waist!\n"
else
msg += "[t_He] [t_has] \icon[belt] \a [belt] about [t_his] waist.\n"
@@ -146,6 +164,8 @@
if(shoes && !skipshoes)
if(shoes.blood_DNA)
msg += "[t_He] [t_is] wearing \icon[shoes] [shoes.gender==PLURAL?"some":"a"] blood-stained [shoes.name] on [t_his] feet!\n"
+ else if(shoes.wasbloody == 2)
+ msg += "[t_He] [t_is] wearing \icon[shoes] [shoes.gender==PLURAL?"some":"a"] luminol-covered [shoes.name] on [t_his] feet!\n"
else
msg += "[t_He] [t_is] wearing \icon[shoes] \a [shoes] on [t_his] feet.\n"
else if(feet_blood_DNA)
@@ -155,6 +175,8 @@
if(wear_mask && !skipmask)
if(wear_mask.blood_DNA)
msg += "[t_He] [t_has] \icon[wear_mask] [wear_mask.gender==PLURAL?"some":"a"] blood-stained [wear_mask.name] on [t_his] face!\n"
+ else if(wear_mask.wasbloody == 2)
+ msg += "[t_He] [t_has] \icon[wear_mask] [wear_mask.gender==PLURAL?"some":"a"] luminol-covered [wear_mask.name] on [t_his] face!\n"
else
msg += "[t_He] [t_has] \icon[wear_mask] \a [wear_mask] on [t_his] face.\n"
@@ -162,6 +184,8 @@
if(glasses && !skipeyes)
if(glasses.blood_DNA)
msg += "[t_He] [t_has] \icon[glasses] [glasses.gender==PLURAL?"some":"a"] blood-stained [glasses] covering [t_his] eyes!\n"
+ else if(glasses.wasbloody == 2)
+ msg += "[t_He] [t_has] \icon[glasses] [glasses.gender==PLURAL?"some":"a"] luminol-covered [glasses] covering [t_his] eyes!\n"
else
msg += "[t_He] [t_has] \icon[glasses] \a [glasses] covering [t_his] eyes.\n"
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index dca63109..4d4a080a 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -1236,7 +1236,7 @@
if(copytext(hud.icon_state,1,4) == "hud") //ugly, but icon comparison is worse, I believe
client.images.Remove(hud)
- client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg)
+ client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.lum)
update_action_buttons()
@@ -1392,6 +1392,9 @@
if(istype(G,/obj/item/clothing/glasses/night))
see_invisible = SEE_INVISIBLE_MINIMUM
client.screen += global_hud.nvg
+ if(istype(G,/obj/item/clothing/glasses/UV))
+ see_invisible = SEE_INVISIBLE_LIVING
+ client.screen += global_hud.lum
/* HUD shit goes here, as long as it doesn't modify sight flags */
// The purpose of this is to stop xray and w/e from preventing you from using huds -- Love, Doohl
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 52576d03..d4d6eb26 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -578,6 +578,11 @@ proc/get_damage_icon_part(damage_state, body_part)
bloodsies.color = w_uniform.blood_color
standing.overlays += bloodsies
+ if(w_uniform.wasbloody == 2)
+ var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "uniformblood")
+ bloodsies.color = "#007fff"
+ standing.overlays += bloodsies
+
if(w_uniform:hastie) //WE CHECKED THE TYPE ABOVE. THIS REALLY SHOULD BE FINE.
var/tie_color = w_uniform:hastie.item_color
if(!tie_color) tie_color = w_uniform:hastie.icon_state
@@ -624,6 +629,10 @@ proc/get_damage_icon_part(damage_state, body_part)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "bloodyhands")
bloodsies.color = gloves.blood_color
standing.overlays += bloodsies
+ else if(gloves.wasbloody == 2)
+ var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "bloodyhands")
+ bloodsies.color = "#007fff"
+ standing.overlays += bloodsies
gloves.screen_loc = ui_gloves
overlays_standing[GLOVES_LAYER] = standing
else
@@ -664,6 +673,10 @@ proc/get_damage_icon_part(damage_state, body_part)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "shoeblood")
bloodsies.color = shoes.blood_color
standing.overlays += bloodsies
+ else if(shoes.wasbloody == 2)
+ var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "shoeblood")
+ bloodsies.color = "#007fff"
+ standing.overlays += bloodsies
overlays_standing[SHOES_LAYER] = standing
else
if(feet_blood_DNA)
@@ -697,6 +710,10 @@ proc/get_damage_icon_part(damage_state, body_part)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "helmetblood")
bloodsies.color = head.blood_color
standing.overlays += bloodsies
+ if(head.wasbloody == 2)
+ var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "helmetblood")
+ bloodsies.color = "#007fff"
+ standing.overlays += bloodsies
overlays_standing[HEAD_LAYER] = standing
else
overlays_standing[HEAD_LAYER] = null
@@ -729,6 +746,12 @@ proc/get_damage_icon_part(damage_state, body_part)
bloodsies.color = wear_suit.blood_color
standing.overlays += bloodsies
+ if(wear_suit.wasbloody == 2)
+ var/obj/item/clothing/suit/S = wear_suit
+ var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "[S.blood_overlay_type]blood")
+ bloodsies.color = "#007fff"
+ standing.overlays += bloodsies
+
overlays_standing[SUIT_LAYER] = standing
update_tail_showing(0)
@@ -756,6 +779,10 @@ proc/get_damage_icon_part(damage_state, body_part)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "maskblood")
bloodsies.color = wear_mask.blood_color
standing.overlays += bloodsies
+ else if( !istype(wear_mask, /obj/item/clothing/mask/cigarette) && wear_mask.wasbloody == 2 )
+ var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "maskblood")
+ bloodsies.color = wear_suit.blood_color
+ standing.overlays += bloodsies
overlays_standing[FACEMASK_LAYER] = standing
else
overlays_standing[FACEMASK_LAYER] = null
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index a599b678..8ffbdb21 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -30,6 +30,7 @@
var/t_oxygen = null
var/t_sl_gas = null
var/t_n2 = null
+ var/gsr = 0 //forensics variable for gunshot residue
var/now_pushing = null
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm
index 117c8b6c..35fc851b 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/mob/new_player/preferences_setup.dm
@@ -600,13 +600,11 @@ datum/preferences
if(4)
clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY)
if(DETECTIVE)
- clothes_s = new /icon('icons/mob/uniform.dmi', "detective_s")
- clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY)
+ clothes_s = new /icon('icons/mob/uniform.dmi', "wardentanclothes_s")
+ clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY)
clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY)
if(prob(1))
clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigaron"), ICON_OVERLAY)
- clothes_s.Blend(new /icon('icons/mob/head.dmi', "detective"), ICON_OVERLAY)
- clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY)
switch(backbag)
if(2)
clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY)
@@ -614,6 +612,20 @@ datum/preferences
clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY)
if(4)
clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY)
+
+ if(FORENSICS)
+ clothes_s = new /icon('icons/mob/uniform.dmi', "polsuit_s")
+ clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY)
+ clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY)
+ clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY)
+ switch(backbag)
+ if(2)
+ clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY)
+ if(3)
+ clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY)
+ if(4)
+ clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY)
+
if(OFFICER)
clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s")
clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 1273e439..f9bee693 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -201,6 +201,15 @@
else
user.update_inv_r_hand()
+ if(istype(src, /obj/item/weapon/gun/projectile) && ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(!H.gloves)
+ H.gsr = 1
+ else
+ var/obj/item/clothing/G = H.gloves
+ G.gsr = 1
+
+
/obj/item/weapon/gun/proc/can_fire()
return load_into_chamber()
diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm
index ae505252..5db22131 100644
--- a/code/modules/reagents/Chemistry-Reagents.dm
+++ b/code/modules/reagents/Chemistry-Reagents.dm
@@ -965,6 +965,62 @@ datum
..()
return
+
+
+ luminol
+ name = "Luminol"
+ id = "luminol"
+ description = "A compound that interacts with blood on the molecular level."
+ reagent_state = LIQUID
+ color = "#F2F3F4"
+ overdose = REAGENTS_OVERDOSE
+
+ reaction_obj(var/obj/O, var/volume)
+ if(istype(O,/obj/effect/decal/cleanable/blood))
+ var/obj/effect/decal/cleanable/blood/W
+ W.invisibility = 1
+ W.color = "#007fff"
+ W.basecolor = "#007fff"
+ else if (istype(O, /obj/item/))
+ var/obj/item/I = O
+ I.reveal_blood()
+ return
+ else
+ return
+
+ reaction_turf(var/turf/T, var/volume)
+ if(volume >= 1)
+ if(istype(T, /turf/simulated))
+ var/turf/simulated/S = T
+ S.dirt = 0
+ T.clean_blood()
+ for(var/obj/effect/decal/cleanable/blood/C in T.contents)
+ C.invisibility = 1
+ C.basecolor = "#007fff"
+ C.color = "#007fff"
+ if(istype(C, /obj/effect/decal/cleanable/blood/tracks/footprints))
+ var/obj/effect/decal/cleanable/blood/tracks/footprints/D = C
+ for(var/datum/fluidtrack/E in D.stack)
+ E.basecolor = "#007fff"
+ D.update_icon()
+ return
+
+ reaction_mob(var/mob/living/carbon/human/H, var/method=TOUCH, var/volume)
+ if(H.head)
+ if(H.head.reveal_blood())
+ H.update_inv_head(0)
+ if(H.wear_suit)
+ if(H.wear_suit.reveal_blood())
+ H.update_inv_wear_suit(0)
+ else if(H.w_uniform)
+ if(H.w_uniform.reveal_blood())
+ H.update_inv_w_uniform(0)
+ if(H.shoes)
+ if(H.shoes.reveal_blood())
+ H.update_inv_shoes(0)
+ else
+ return
+
space_cleaner
name = "Space cleaner"
id = "cleaner"
@@ -974,11 +1030,14 @@ datum
overdose = REAGENTS_OVERDOSE
reaction_obj(var/obj/O, var/volume)
- if(istype(O,/obj/effect/decal/cleanable))
+ if(istype(O,/obj/effect/decal/cleanable/blood))
+ O.invisibility = INVISIBILITY_MAXIMUM
+ return
+ else if(istype(O,/obj/effect/decal/cleanable/))
del(O)
else
- if(O)
- O.clean_blood()
+ O.clean_blood()
+ return
reaction_turf(var/turf/T, var/volume)
if(volume >= 1)
@@ -987,8 +1046,13 @@ datum
S.dirt = 0
T.clean_blood()
for(var/obj/effect/decal/cleanable/C in T.contents)
- src.reaction_obj(C, volume)
- del(C)
+ if(istype (C,/obj/effect/decal/cleanable/blood))
+ src.reaction_obj(C, volume)
+ C.invisibility = INVISIBILITY_MAXIMUM
+ return
+ else
+ src.reaction_obj(C, volume)
+ del(C)
for(var/mob/living/carbon/slime/M in T)
M.adjustToxLoss(rand(5,10))
diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm
index 323c0960..109684ca 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/Chemistry-Recipes.dm
@@ -713,6 +713,12 @@ datum
required_reagents = list("ammonia" = 1, "water" = 1)
result_amount = 2
+ luminol
+ name = "Luminol"
+ id = "luminol"
+ result = "luminol"
+ required_reagents = list("hydrogen" = 3, "carbon" = 3, "ammonia" = 3)
+
plantbgone
name = "Plant-B-Gone"
id = "plantbgone"
diff --git a/icons/mob/items_lefthand.dmi b/icons/mob/items_lefthand.dmi
index 8754031b..7f5064f9 100644
Binary files a/icons/mob/items_lefthand.dmi and b/icons/mob/items_lefthand.dmi differ
diff --git a/icons/mob/items_righthand.dmi b/icons/mob/items_righthand.dmi
index 21b68bc3..a03017a2 100644
Binary files a/icons/mob/items_righthand.dmi and b/icons/mob/items_righthand.dmi differ
diff --git a/icons/obj/forensics.dmi b/icons/obj/forensics.dmi
new file mode 100644
index 00000000..63225c30
Binary files /dev/null and b/icons/obj/forensics.dmi differ
diff --git a/icons/obj/nvg_hud_full.dmi b/icons/obj/nvg_hud_full.dmi
index b9f8ad97..8c87ff72 100644
Binary files a/icons/obj/nvg_hud_full.dmi and b/icons/obj/nvg_hud_full.dmi differ
diff --git a/nano/templates/dnaforensics.tmpl b/nano/templates/dnaforensics.tmpl
new file mode 100644
index 00000000..04b203e4
--- /dev/null
+++ b/nano/templates/dnaforensics.tmpl
@@ -0,0 +1,42 @@
+