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 @@ +

Machine Status

+
+
+ {{:~link(scanning ? 'Halt Scan' : 'Begin Scan', 'signal-diag', {'scanItem' : 1}, null)}} +
+
+ {{:~link('Eject item', 'eject', {'ejectItem' : 1}, (bloodsamp && !scanning) ? null : 'disabled')}} +
+
+

Scanner

+
+
Scan progress:
+
+ {{:~displayBar(scan_progress, 0, 100, 'good')}} + {{:scan_progress}} % +
+
+ {{if scan_progress >= 100}} + Scan completed successfully. + {{/if}} +
+
+
+
+
Item:
+
+ {{if bloodsamp}} + {{:bloodsamp}} + {{else}} + No item inserted + {{/if}} +
+
+
+
Heuristic analysis:
+
+ {{if bloodsamp_desc}} + {{:bloodsamp_desc}} + {{/if}} +
+
+
\ No newline at end of file