diff --git a/code/__DEFINES/blood.dm b/code/__DEFINES/blood.dm index b3b8c0d1f3d..320c3ded51a 100644 --- a/code/__DEFINES/blood.dm +++ b/code/__DEFINES/blood.dm @@ -1,6 +1,3 @@ -/// Checks if an object is covered in blood -#define HAS_BLOOD_DNA(thing) (length(thing.GetComponent(/datum/component/forensics)?.blood_DNA)) - //Bloody shoes/footprints /// Minimum alpha of footprints #define BLOODY_FOOTPRINT_BASE_ALPHA 20 @@ -20,5 +17,5 @@ #define BLOOD_STATE_XENO "xeno" /// Black robot oil #define BLOOD_STATE_OIL "oil" -/// No blood is present +/// No blood is present #define BLOOD_STATE_NOT_BLOODY "no blood whatsoever" diff --git a/code/__HELPERS/forensics.dm b/code/__HELPERS/forensics.dm new file mode 100644 index 00000000000..2d43a1ad61b --- /dev/null +++ b/code/__HELPERS/forensics.dm @@ -0,0 +1,10 @@ +/// Returns the fingerprints on this atom +#define GET_ATOM_FINGERPRINTS(atom) atom.forensics?.fingerprints +/// Returns the hidden prints on this atom +#define GET_ATOM_HIDDENPRINTS(atom) atom.forensics?.hiddenprints +/// Returns the blood dna on this atom +#define GET_ATOM_BLOOD_DNA(atom) atom.forensics?.blood_DNA +/// Returns the fibers on this atom +#define GET_ATOM_FIBRES(atom) atom.forensics?.fibers +/// Returns the number of unique blood dna sources on this atom +#define GET_ATOM_BLOOD_DNA_LENGTH(atom) (isnull(atom.forensics) ? 0 : length(atom.forensics.blood_DNA)) diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm index ac7e9f363ae..7e8f0d1ab99 100644 --- a/code/datums/brain_damage/phobia.dm +++ b/code/datums/brain_damage/phobia.dm @@ -48,13 +48,13 @@ var/list/seen_atoms = view(7, owner) if(LAZYLEN(trigger_objs)) for(var/obj/O in seen_atoms) - if(is_type_in_typecache(O, trigger_objs) || (phobia_type == "blood" && HAS_BLOOD_DNA(O))) + if(is_type_in_typecache(O, trigger_objs) || (phobia_type == "blood" && GET_ATOM_BLOOD_DNA_LENGTH(O))) freak_out(O) return for(var/mob/living/carbon/human/HU in seen_atoms) //check equipment for trigger items for(var/X in HU.get_all_worn_items() | HU.held_items) var/obj/I = X - if(!QDELETED(I) && (is_type_in_typecache(I, trigger_objs) || (phobia_type == "blood" && HAS_BLOOD_DNA(I)))) + if(!QDELETED(I) && (is_type_in_typecache(I, trigger_objs) || (phobia_type == "blood" && GET_ATOM_BLOOD_DNA_LENGTH(I)))) freak_out(I) return diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm index 1c8969cfec1..9268385775f 100644 --- a/code/datums/components/bloodysoles.dm +++ b/code/datums/components/bloodysoles.dm @@ -71,7 +71,7 @@ if(HAS_TRAIT(parent_atom, TRAIT_LIGHT_STEP)) //the character is agile enough to don't mess their clothing and hands just from one blood splatter at floor return TRUE - parent_atom.add_blood_DNA(pool.return_blood_DNA()) + parent_atom.add_blood_DNA(GET_ATOM_BLOOD_DNA(pool)) update_icon() /** @@ -156,7 +156,7 @@ oldLocFP.exited_dirs |= wielder.dir add_parent_to_footprint(oldLocFP) oldLocFP.bloodiness = half_our_blood - oldLocFP.add_blood_DNA(parent_atom.return_blood_DNA()) + oldLocFP.add_blood_DNA(GET_ATOM_BLOOD_DNA(parent_atom)) oldLocFP.update_appearance() half_our_blood = bloody_shoes[last_blood_state] / 2 @@ -176,7 +176,7 @@ FP.entered_dirs |= wielder.dir add_parent_to_footprint(FP) FP.bloodiness = half_our_blood - FP.add_blood_DNA(parent_atom.return_blood_DNA()) + FP.add_blood_DNA(GET_ATOM_BLOOD_DNA(parent_atom)) FP.update_appearance() diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm deleted file mode 100644 index 26b3d9f5c14..00000000000 --- a/code/datums/components/forensics.dm +++ /dev/null @@ -1,188 +0,0 @@ -/datum/component/forensics - dupe_mode = COMPONENT_DUPE_UNIQUE - can_transfer = TRUE - var/list/fingerprints //assoc print = print - var/list/hiddenprints //assoc ckey = realname/gloves/ckey - var/list/blood_DNA //assoc dna = bloodtype - var/list/fibers //assoc print = print - -/datum/component/forensics/InheritComponent(datum/component/forensics/F, original) //Use of | and |= being different here is INTENTIONAL. - fingerprints = LAZY_LISTS_OR(fingerprints, F.fingerprints) - hiddenprints = LAZY_LISTS_OR(hiddenprints, F.hiddenprints) - blood_DNA = LAZY_LISTS_OR(blood_DNA, F.blood_DNA) - fibers = LAZY_LISTS_OR(fibers, F.fibers) - check_blood() - return ..() - -/datum/component/forensics/Initialize(new_fingerprints, new_hiddenprints, new_blood_DNA, new_fibers) - if(!isatom(parent)) - return COMPONENT_INCOMPATIBLE - fingerprints = new_fingerprints - hiddenprints = new_hiddenprints - blood_DNA = new_blood_DNA - fibers = new_fibers - check_blood() - -/datum/component/forensics/RegisterWithParent() - check_blood() - RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_act) - -/datum/component/forensics/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_COMPONENT_CLEAN_ACT)) - -/datum/component/forensics/PostTransfer() - if(!isatom(parent)) - return COMPONENT_INCOMPATIBLE - -/datum/component/forensics/proc/wipe_fingerprints() - fingerprints = null - return TRUE - -/datum/component/forensics/proc/wipe_hiddenprints() - return //no. - -/datum/component/forensics/proc/wipe_blood_DNA() - blood_DNA = null - return TRUE - -/datum/component/forensics/proc/wipe_fibers() - fibers = null - return TRUE - -/datum/component/forensics/proc/clean_act(datum/source, clean_types) - SIGNAL_HANDLER - - . = NONE - if(clean_types & CLEAN_TYPE_FINGERPRINTS) - wipe_fingerprints() - . = COMPONENT_CLEANED - if(clean_types & CLEAN_TYPE_BLOOD) - wipe_blood_DNA() - . = COMPONENT_CLEANED - if(clean_types & CLEAN_TYPE_FIBERS) - wipe_fibers() - . = COMPONENT_CLEANED - -/datum/component/forensics/proc/add_fingerprint_list(list/_fingerprints) //list(text) - if(!length(_fingerprints)) - return - LAZYINITLIST(fingerprints) - for(var/i in _fingerprints) //We use an associative list, make sure we don't just merge a non-associative list into ours. - fingerprints[i] = i - return TRUE - -/datum/component/forensics/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE) - if(!isliving(M)) - if(!iscameramob(M)) - return - if(isaicamera(M)) - var/mob/camera/ai_eye/ai_camera = M - if(!ai_camera.ai) - return - M = ai_camera.ai - add_hiddenprint(M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - add_fibers(H) - var/obj/item/gloves = H.gloves - if(gloves) //Check if the gloves (if any) hide fingerprints - if(!(gloves.body_parts_covered & HANDS) || HAS_TRAIT(gloves, TRAIT_FINGERPRINT_PASSTHROUGH) || HAS_TRAIT(H, TRAIT_FINGERPRINT_PASSTHROUGH)) - ignoregloves = TRUE - if(!ignoregloves) - H.gloves.add_fingerprint(H, TRUE) //ignoregloves = 1 to avoid infinite loop. - return - var/full_print = md5(H.dna.unique_identity) - LAZYSET(fingerprints, full_print, full_print) - return TRUE - -/datum/component/forensics/proc/add_fiber_list(list/_fibertext) //list(text) - if(!length(_fibertext)) - return - LAZYINITLIST(fibers) - for(var/i in _fibertext) //We use an associative list, make sure we don't just merge a non-associative list into ours. - fibers[i] = i - return TRUE - -/datum/component/forensics/proc/add_fibers(mob/living/carbon/human/M) - var/fibertext - var/item_multiplier = isitem(src)?1.2:1 - if(M.wear_suit) - fibertext = "Material from \a [M.wear_suit]." - if(prob(10*item_multiplier) && !LAZYACCESS(fibers, fibertext)) - LAZYSET(fibers, fibertext, fibertext) - if(!(M.wear_suit.body_parts_covered & CHEST)) - if(M.w_uniform) - fibertext = "Fibers from \a [M.w_uniform]." - if(prob(12*item_multiplier) && !LAZYACCESS(fibers, fibertext)) //Wearing a suit means less of the uniform exposed. - LAZYSET(fibers, fibertext, fibertext) - if(!(M.wear_suit.body_parts_covered & HANDS)) - if(M.gloves) - fibertext = "Material from a pair of [M.gloves.name]." - if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext)) - LAZYSET(fibers, fibertext, fibertext) - else if(M.w_uniform) - fibertext = "Fibers from \a [M.w_uniform]." - if(prob(15*item_multiplier) && !LAZYACCESS(fibers, fibertext)) - // "Added fibertext: [fibertext]" - LAZYSET(fibers, fibertext, fibertext) - if(M.gloves) - fibertext = "Material from a pair of [M.gloves.name]." - if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext)) - LAZYSET(fibers, fibertext, fibertext) - else if(M.gloves) - fibertext = "Material from a pair of [M.gloves.name]." - if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext)) - LAZYSET(fibers, fibertext, fibertext) - return TRUE - -/datum/component/forensics/proc/add_hiddenprint_list(list/_hiddenprints) //list(ckey = text) - if(!length(_hiddenprints)) - return - LAZYINITLIST(hiddenprints) - for(var/i in _hiddenprints) //We use an associative list, make sure we don't just merge a non-associative list into ours. - hiddenprints[i] = _hiddenprints[i] - return TRUE - -/datum/component/forensics/proc/add_hiddenprint(mob/M) - if(!isliving(M)) - if(!iscameramob(M)) - return - if(isaicamera(M)) - var/mob/camera/ai_eye/ai_camera = M - if(!ai_camera.ai) - return - M = ai_camera.ai - if(!M.key) - return - var/hasgloves = "" - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.gloves) - hasgloves = "(gloves)" - var/current_time = time_stamp() - if(!LAZYACCESS(hiddenprints, M.key)) - LAZYSET(hiddenprints, M.key, "First: \[[current_time]\] \"[M.real_name]\"[hasgloves]. Ckey: [M.ckey]") - else - var/laststamppos = findtext(LAZYACCESS(hiddenprints, M.key), "\nLast: ") - if(laststamppos) - LAZYSET(hiddenprints, M.key, copytext(hiddenprints[M.key], 1, laststamppos)) - hiddenprints[M.key] += "\nLast: \[[current_time]\] \"[M.real_name]\"[hasgloves]. Ckey: [M.ckey]" //made sure to be existing by if(!LAZYACCESS);else - var/atom/A = parent - A.fingerprintslast = M.ckey - return TRUE - -/datum/component/forensics/proc/add_blood_DNA(list/dna) //list(dna_enzymes = type) - if(!length(dna)) - return - LAZYINITLIST(blood_DNA) - for(var/i in dna) - blood_DNA[i] = dna[i] - check_blood() - return TRUE - -/datum/component/forensics/proc/check_blood() - if(!isitem(parent)) - return - if(!length(blood_DNA)) - return - parent.AddElement(/datum/element/decal/blood) diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index d1119a4fa44..57b40b12476 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -53,7 +53,7 @@ if(sniffed) var/old_target = tracking_target possible = list() - var/list/prints = sniffed.return_fingerprints() + var/list/prints = GET_ATOM_FINGERPRINTS(sniffed) if(prints) for(var/mob/living/carbon/C in GLOB.carbon_list) if(prints[md5(C.dna.unique_identity)]) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 8627ccc53c5..b4ddb1d4709 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -159,6 +159,8 @@ var/damage_deflection = 0 var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF + /// forensics datum, contains fingerprints, fibres, blood_dna and hiddenprints on this atom + var/datum/forensics/forensics /** * Called when an atom is created in byond (built in engine proc) @@ -309,6 +311,9 @@ if(reagents) QDEL_NULL(reagents) + if(forensics) + QDEL_NULL(forensics) + orbiters = null // The component is attached to us normaly and will be deleted elsewhere LAZYCLEARLIST(overlays) @@ -900,9 +905,9 @@ var/new_blood_dna = injected_mob.get_blood_dna_list() if(!new_blood_dna) return FALSE - var/old_length = blood_DNA_length() + var/old_length = GET_ATOM_BLOOD_DNA_LENGTH(src) add_blood_DNA(new_blood_dna) - if(blood_DNA_length() == old_length) + if(GET_ATOM_BLOOD_DNA_LENGTH(src) == old_length) return FALSE return TRUE diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index a35d8cc642d..c0701ae6b68 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -52,7 +52,7 @@ return TRUE /obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C) - C.add_blood_DNA(return_blood_DNA()) + C.add_blood_DNA(GET_ATOM_BLOOD_DNA(src)) if (bloodiness) C.bloodiness = min((C.bloodiness + bloodiness), BLOOD_AMOUNT_PER_DECAL) return ..() diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 743f290782b..c98ab48efcf 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -687,9 +687,9 @@ . = ..() /obj/item/stack/proc/copy_evidences(obj/item/stack/from) - add_blood_DNA(from.return_blood_DNA()) - add_fingerprint_list(from.return_fingerprints()) - add_hiddenprint_list(from.return_hiddenprints()) + add_blood_DNA(GET_ATOM_BLOOD_DNA(from)) + add_fingerprint_list(GET_ATOM_FINGERPRINTS(from)) + add_hiddenprint_list(GET_ATOM_HIDDENPRINTS(from)) fingerprintslast = from.fingerprintslast //TODO bloody overlay diff --git a/code/modules/admin/verbs/hiddenprints.dm b/code/modules/admin/verbs/hiddenprints.dm index 2d87e404498..500792cc42a 100644 --- a/code/modules/admin/verbs/hiddenprints.dm +++ b/code/modules/admin/verbs/hiddenprints.dm @@ -3,7 +3,7 @@ return var/interface = "A log of every player who has touched [victim], sorted by last touch.

    " - var/victim_hiddenprints = victim.return_hiddenprints() + var/victim_hiddenprints = GET_ATOM_HIDDENPRINTS(victim) if(!islist(victim_hiddenprints)) victim_hiddenprints = list() diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index c3e9f086aa7..8f0ded9411a 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -373,7 +373,7 @@ /datum/heretic_knowledge/curse/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) fingerprints = list() for(var/atom/requirements as anything in atoms) - fingerprints[requirements.return_fingerprints()] = 1 + fingerprints[GET_ATOM_FINGERPRINTS(requirements)] = 1 list_clear_nulls(fingerprints) // No fingerprints? No ritual diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index ab008846a5c..f1111075c2b 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -32,7 +32,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) . += mutable_appearance('icons/effects/blood.dmi', "bloodyhands") /obj/item/clothing/gloves/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED) diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm index 83585452208..b6f8596f05d 100644 --- a/code/modules/clothing/head/_head.dm +++ b/code/modules/clothing/head/_head.dm @@ -65,7 +65,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) if(clothing_flags & LARGE_WORN_ICON) . += mutable_appearance('icons/effects/64x64.dmi', "helmetblood_large") else diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm index d9c7dc8bae6..a0007239abc 100644 --- a/code/modules/clothing/masks/_masks.dm +++ b/code/modules/clothing/masks/_masks.dm @@ -50,7 +50,7 @@ if(body_parts_covered & HEAD) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) . += mutable_appearance('icons/effects/blood.dmi', "maskblood") /obj/item/clothing/mask/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED) diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 8833b0cdeb8..4a6f898d43b 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -14,7 +14,7 @@ if(body_parts_covered & HEAD) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) . += mutable_appearance('icons/effects/blood.dmi', "maskblood") /obj/item/clothing/neck/tie diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index c0a618089f8..4dd532f3227 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -48,7 +48,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) if(clothing_flags & LARGE_WORN_ICON) . += mutable_appearance('icons/effects/64x64.dmi', "shoeblood_large") else diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index 40dadad345b..ce676017ea1 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -26,7 +26,7 @@ var/damagefile2use = (mutant_styles & STYLE_TAUR_ALL) ? 'modular_skyrat/master_files/icons/mob/64x32_item_damage.dmi' : 'icons/effects/item_damage.dmi' . += mutable_appearance(damagefile2use, "damaged[blood_overlay_type]") //SKYRAT EDIT CHANGE END - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) //SKYRAT EDIT CHANGE BEGIN //. += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood") //ORIGINAL var/bloodfile2use = (mutant_styles & STYLE_TAUR_ALL) ? 'modular_skyrat/master_files/icons/mob/64x32_blood.dmi' : 'icons/effects/blood.dmi' diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index 0a93e179213..547dc74e696 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -34,7 +34,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA_LENGTH(src)) . += mutable_appearance('icons/effects/blood.dmi', "uniformblood") if(accessory_overlay) . += accessory_overlay diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index f3af29917d6..83513f44fb1 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -86,8 +86,8 @@ //Make our lists var/list/fingerprints = list() - var/list/blood = A.return_blood_DNA() - var/list/fibers = A.return_fibers() + var/list/blood = GET_ATOM_BLOOD_DNA(A) + var/list/fibers = GET_ATOM_FIBRES(A) var/list/reagents = list() var/target_name = A.name @@ -102,7 +102,7 @@ else if(!ismob(A)) - fingerprints = A.return_fingerprints() + fingerprints = GET_ATOM_FINGERPRINTS(A) // Only get reagents from non-mobs. if(A.reagents && A.reagents.reagent_list.len) diff --git a/code/modules/forensics/_forensics.dm b/code/modules/forensics/_forensics.dm new file mode 100644 index 00000000000..70d7699b3bb --- /dev/null +++ b/code/modules/forensics/_forensics.dm @@ -0,0 +1,237 @@ +/** + * Forensics datum + * + * Placed onto atoms, and contains: + * * List of fingerprints on the atom + * * List of hidden prints (used for admins) + * * List of blood on the atom + * * List of clothing fibers on the atom + */ +/datum/forensics + /// Weakref to the parent owning this datum + var/datum/weakref/parent + /** + * List of fingerprints on this atom + * + * Formatting: + * * print = print + */ + var/list/fingerprints + /** + * List of hiddenprints on this atom + * + * Formatting: + * * ckey = realname/gloves/ckey + */ + var/list/hiddenprints + /** + * List of blood dna on this atom + * + * Formatting: + * * dna = bloodtype + */ + var/list/blood_DNA + /** + * List of clothing fibers on this atom + * + * Formatting: + * * fiber = fiber + */ + var/list/fibers + +/datum/forensics/New(atom/parent, fingerprints, hiddenprints, blood_DNA, fibers) + if(!isatom(parent)) + stack_trace("We tried adding a forensics datum to something that isnt an atom. What the hell are you doing?") + qdel(src) + return + + RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_act) + + src.parent = WEAKREF(parent) + src.fingerprints = fingerprints + src.hiddenprints = hiddenprints + src.blood_DNA = blood_DNA + src.fibers = fibers + check_blood() + +/// Merges the given lists into the preexisting values +/datum/forensics/proc/inherit_new(list/fingerprints, list/hiddenprints, list/blood_DNA, list/fibers) //Use of | and |= being different here is INTENTIONAL. + if (fingerprints) + src.fingerprints = LAZY_LISTS_OR(src.fingerprints, fingerprints) + if (hiddenprints) + src.hiddenprints = LAZY_LISTS_OR(src.hiddenprints, hiddenprints) + if (blood_DNA) + src.blood_DNA = LAZY_LISTS_OR(src.blood_DNA, blood_DNA) + if (fibers) + src.fibers = LAZY_LISTS_OR(src.fibers, fibers) + check_blood() + +/datum/forensics/Destroy(force, ...) + var/atom/parent_atom = parent.resolve() + if (!isnull(parent_atom)) + UnregisterSignal(parent_atom, list(COMSIG_COMPONENT_CLEAN_ACT)) + return ..() + +/// Empties the fingerprints list +/datum/forensics/proc/wipe_fingerprints() + fingerprints = null + return TRUE + +/// Empties the blood_DNA list +/datum/forensics/proc/wipe_blood_DNA() + blood_DNA = null + return TRUE + +/// Empties the fibers list +/datum/forensics/proc/wipe_fibers() + fibers = null + return TRUE + +/// Handles cleaning up the various forensic types +/datum/forensics/proc/clean_act(datum/source, clean_types) + SIGNAL_HANDLER + + if(clean_types & CLEAN_TYPE_FINGERPRINTS) + wipe_fingerprints() + if(clean_types & CLEAN_TYPE_BLOOD) + wipe_blood_DNA() + if(clean_types & CLEAN_TYPE_FIBERS) + wipe_fibers() + +/// Adds the given list into fingerprints +/datum/forensics/proc/add_fingerprint_list(list/fingerprints) + if(!length(fingerprints)) + return + LAZYINITLIST(src.fingerprints) + for(var/fingerprint in fingerprints) //We use an associative list, make sure we don't just merge a non-associative list into ours. + src.fingerprints[fingerprint] = fingerprint + return TRUE + +/// Adds a single fingerprint +/datum/forensics/proc/add_fingerprint(mob/living/suspect, ignoregloves = FALSE) + if(!isliving(suspect)) + if(!iscameramob(suspect)) + return + if(isaicamera(suspect)) + var/mob/camera/ai_eye/ai_camera = suspect + if(!ai_camera.ai) + return + suspect = ai_camera.ai + add_hiddenprint(suspect) + if(ishuman(suspect)) + var/mob/living/carbon/human/human_suspect = suspect + add_fibers(human_suspect) + var/obj/item/gloves = human_suspect.gloves + if(gloves) //Check if the gloves (if any) hide fingerprints + if(!(gloves.body_parts_covered & HANDS) || HAS_TRAIT(gloves, TRAIT_FINGERPRINT_PASSTHROUGH) || HAS_TRAIT(human_suspect, TRAIT_FINGERPRINT_PASSTHROUGH)) + ignoregloves = TRUE + if(!ignoregloves) + human_suspect.gloves.add_fingerprint(human_suspect, ignoregloves = TRUE) //ignoregloves = TRUE to avoid infinite loop. + return + var/full_print = md5(human_suspect.dna.unique_identity) + LAZYSET(fingerprints, full_print, full_print) + return TRUE + +/// Adds the given list into fibers +/datum/forensics/proc/add_fiber_list(list/fibers) + if(!length(fibers)) + return + LAZYINITLIST(src.fibers) + for(var/fiber in fibers) //We use an associative list, make sure we don't just merge a non-associative list into ours. + src.fibers[fiber] = fiber + return TRUE + +#define ITEM_FIBER_MULTIPLIER 1.2 +#define NON_ITEM_FIBER_MULTIPLIER 1 + +/// Adds a single fiber +/datum/forensics/proc/add_fibers(mob/living/carbon/human/suspect) + var/fibertext + var/item_multiplier = isitem(src) ? ITEM_FIBER_MULTIPLIER : NON_ITEM_FIBER_MULTIPLIER + if(suspect.wear_suit) + fibertext = "Material from \a [suspect.wear_suit]." + if(prob(10 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + if(!(suspect.wear_suit.body_parts_covered & CHEST)) + if(suspect.w_uniform) + fibertext = "Fibers from \a [suspect.w_uniform]." + if(prob(12 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) //Wearing a suit means less of the uniform exposed. + LAZYSET(fibers, fibertext, fibertext) + if(!(suspect.wear_suit.body_parts_covered & HANDS)) + if(suspect.gloves) + fibertext = "Material from a pair of [suspect.gloves.name]." + if(prob(20 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + else if(suspect.w_uniform) + fibertext = "Fibers from \a [suspect.w_uniform]." + if(prob(15 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + if(suspect.gloves) + fibertext = "Material from a pair of [suspect.gloves.name]." + if(prob(20 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + else if(suspect.gloves) + fibertext = "Material from a pair of [suspect.gloves.name]." + if(prob(20 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + return TRUE + +#undef ITEM_FIBER_MULTIPLIER +#undef NON_ITEM_FIBER_MULTIPLIER + +/// Adds the given list into hiddenprints +/datum/forensics/proc/add_hiddenprint_list(list/hiddenprints) //list(ckey = text) + if(!length(hiddenprints)) + return + LAZYINITLIST(src.hiddenprints) + for(var/hidden_print in hiddenprints) //We use an associative list, make sure we don't just merge a non-associative list into ours. + src.hiddenprints[hidden_print] = hiddenprints[hidden_print] + return TRUE + +/// Adds a single hiddenprint +/datum/forensics/proc/add_hiddenprint(mob/suspect) + if(!isliving(suspect)) + if(!iscameramob(suspect)) + return + if(isaicamera(suspect)) + var/mob/camera/ai_eye/ai_camera = suspect + if(!ai_camera.ai) + return + suspect = ai_camera.ai + if(!suspect.key) + return + var/has_gloves = "" + if(ishuman(suspect)) + var/mob/living/carbon/human/human_suspect = suspect + if(human_suspect.gloves) + has_gloves = "(gloves)" + var/current_time = time_stamp() + if(!LAZYACCESS(hiddenprints, suspect.key)) + LAZYSET(hiddenprints, suspect.key, "First: \[[current_time]\] \"[suspect.real_name]\"[has_gloves]. Ckey: [suspect.ckey]") + else + var/last_stamp_pos = findtext(LAZYACCESS(hiddenprints, suspect.key), "\nLast: ") + if(last_stamp_pos) + LAZYSET(hiddenprints, suspect.key, copytext(hiddenprints[suspect.key], 1, last_stamp_pos)) + hiddenprints[suspect.key] += "\nLast: \[[current_time]\] \"[suspect.real_name]\"[has_gloves]. Ckey: [suspect.ckey]" //made sure to be existing by if(!LAZYACCESS);else + var/atom/parent_atom = parent.resolve() + parent_atom.fingerprintslast = suspect.ckey + return TRUE + +/// Adds the given list into blood_DNA +/datum/forensics/proc/add_blood_DNA(list/blood_DNA) + if(!length(blood_DNA)) + return + LAZYINITLIST(src.blood_DNA) + for(var/gene in blood_DNA) + src.blood_DNA[gene] = blood_DNA[gene] + check_blood() + return TRUE + +/// Updates the blood displayed on parent +/datum/forensics/proc/check_blood() + if(!isitem(parent.resolve())) + return + if(!length(blood_DNA)) + return + var/atom/parent_atom = parent.resolve() + parent_atom.AddElement(/datum/element/decal/blood) diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm new file mode 100644 index 00000000000..155c6ab6788 --- /dev/null +++ b/code/modules/forensics/forensics_helpers.dm @@ -0,0 +1,122 @@ +/// Adds a list of fingerprints to the atom +/atom/proc/add_fingerprint_list(list/fingerprints_to_add) //ASSOC LIST FINGERPRINT = FINGERPRINT + if (isnull(fingerprints_to_add)) + return + if (forensics) + forensics.inherit_new(fingerprints = fingerprints_to_add) + else + forensics = new(src, fingerprints = fingerprints_to_add) + return TRUE + +/// Adds a single fingerprint to the atom +/atom/proc/add_fingerprint(mob/suspect, ignoregloves = FALSE) //Set ignoregloves to add prints irrespective of the mob having gloves on. + if (QDELING(src)) + return + if (isnull(forensics)) + forensics = new(src) + forensics.add_fingerprint(suspect, ignoregloves) + return TRUE + +/// Add a list of fibers to the atom +/atom/proc/add_fiber_list(list/fibers_to_add) //ASSOC LIST FIBERTEXT = FIBERTEXT + if (isnull(fibers_to_add)) + return + if (forensics) + forensics.inherit_new(fibers = fibers_to_add) + else + forensics = new(src, fibers = fibers_to_add) + return TRUE + +/// Adds a single fiber to the atom +/atom/proc/add_fibers(mob/living/carbon/human/suspect) + var/old = 0 + if(suspect.gloves && istype(suspect.gloves, /obj/item/clothing)) + var/obj/item/clothing/gloves/suspect_gloves = suspect.gloves + old = length(GET_ATOM_BLOOD_DNA(suspect_gloves)) + if(suspect_gloves.transfer_blood > 1) //bloodied gloves transfer blood to touched objects + if(add_blood_DNA(GET_ATOM_BLOOD_DNA(suspect_gloves)) && GET_ATOM_BLOOD_DNA_LENGTH(suspect_gloves) > old) //only reduces the bloodiness of our gloves if the item wasn't already bloody + suspect_gloves.transfer_blood -= 1 + else if(suspect.blood_in_hands > 1) + old = length(GET_ATOM_BLOOD_DNA(suspect)) + if(add_blood_DNA(GET_ATOM_BLOOD_DNA(suspect)) && GET_ATOM_BLOOD_DNA_LENGTH(suspect) > old) + suspect.blood_in_hands -= 1 + if (isnull(forensics)) + forensics = new(src) + forensics.add_fibers(suspect) + return TRUE + +/// Adds a list of hiddenprints to the atom +/atom/proc/add_hiddenprint_list(list/hiddenprints_to_add) //NOTE: THIS IS FOR ADMINISTRATION FINGERPRINTS, YOU MUST CUSTOM SET THIS TO INCLUDE CKEY/REAL NAMES! CHECK FORENSICS.DM + if (isnull(hiddenprints_to_add)) + return + if (forensics) + forensics.inherit_new(hiddenprints = hiddenprints_to_add) + else + forensics = new(src, hiddenprints = hiddenprints_to_add) + return TRUE + +/// Adds a single hiddenprint to the atom +/atom/proc/add_hiddenprint(mob/suspect) + if (isnull(forensics)) + forensics = new(src) + forensics.add_hiddenprint(suspect) + return TRUE + +/// Adds blood dna to the atom +/atom/proc/add_blood_DNA(list/blood_DNA_to_add) //ASSOC LIST DNA = BLOODTYPE + return FALSE + +/obj/add_blood_DNA(list/blood_DNA_to_add) + . = ..() + if (isnull(blood_DNA_to_add)) + return . + if (forensics) + forensics.inherit_new(blood_DNA = blood_DNA_to_add) + else + forensics = new(src, blood_DNA = blood_DNA_to_add) + return TRUE + +/obj/item/clothing/gloves/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + transfer_blood = rand(2, 4) + return ..() + +/turf/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + var/obj/effect/decal/cleanable/blood/splatter/blood_splatter = locate() in src + if(!blood_splatter) + blood_splatter = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) + if(!QDELETED(blood_splatter)) + blood_splatter.add_blood_DNA(blood_dna) //give blood info to the blood decal. + return TRUE //we bloodied the floor + return FALSE + +/mob/living/carbon/human/add_blood_DNA(list/blood_DNA_to_add, list/datum/disease/diseases) + if(wear_suit) + wear_suit.add_blood_DNA(blood_DNA_to_add) + update_inv_wear_suit() + else if(w_uniform) + w_uniform.add_blood_DNA(blood_DNA_to_add) + update_inv_w_uniform() + if(gloves) + var/obj/item/clothing/gloves/mob_gloves = gloves + mob_gloves.add_blood_DNA(blood_DNA_to_add) + else if(length(blood_DNA_to_add)) + if (isnull(forensics)) + forensics = new(src) + forensics.inherit_new(blood_DNA = blood_DNA_to_add) + blood_in_hands = rand(2, 4) + update_inv_gloves() + return TRUE + +/* + * Transfer all the fingerprints and hidden prints from [src] to [transfer_to]. + */ +/atom/proc/transfer_fingerprints_to(atom/transfer_to) + transfer_to.add_fingerprint_list(GET_ATOM_FINGERPRINTS(src)) + transfer_to.add_hiddenprint_list(GET_ATOM_HIDDENPRINTS(src)) + transfer_to.fingerprintslast = fingerprintslast + +/* + * Transfer all the fibers from [src] to [transfer_to]. + */ +/atom/proc/transfer_fibers_to(atom/transfer_to) + transfer_to.add_fiber_list(GET_ATOM_FIBRES(src)) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 8077ec397ce..3d989815694 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -327,7 +327,7 @@ drop.transfer_mob_blood_dna(src) return else - temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter + temp_blood_DNA = GET_ATOM_BLOOD_DNA(drop) //we transfer the dna from the drip to the splatter qdel(drop)//the drip is replaced by a bigger splatter else T.PolluteTurf(/datum/pollutant/metallic_scent, 5) //SKYRAT EDIT ADDITION diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index c6f32850b8f..ad43a969390 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1419,7 +1419,7 @@ if(!isturf(loc)) return var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc) - our_splatter.add_blood_DNA(return_blood_DNA()) + our_splatter.add_blood_DNA(GET_ATOM_BLOOD_DNA(src)) our_splatter.blood_dna_info = get_blood_dna_list() var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength) our_splatter.fly_towards(targ, splatter_strength) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 3725559a56c..eba07172fc4 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -82,11 +82,10 @@ if(!(I.item_flags & ABSTRACT) && !(I.item_flags & EXAMINE_SKIP)) . += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))]." - var/datum/component/forensics/FR = GetComponent(/datum/component/forensics) //gloves if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP)) . += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands." - else if(FR && length(FR.blood_DNA)) + else if(GET_ATOM_BLOOD_DNA_LENGTH(src)) if(num_hands) . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a"] blood-stained hand[num_hands > 1 ? "s" : ""]!") diff --git a/code/modules/mob/living/simple_animal/bot/hygienebot.dm b/code/modules/mob/living/simple_animal/bot/hygienebot.dm index a1d9ab81793..f4527a55902 100644 --- a/code/modules/mob/living/simple_animal/bot/hygienebot.dm +++ b/code/modules/mob/living/simple_animal/bot/hygienebot.dm @@ -211,7 +211,7 @@ for(var/X in list(ITEM_SLOT_HEAD, ITEM_SLOT_MASK, ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING, ITEM_SLOT_FEET)) var/obj/item/I = L.get_item_by_slot(X) - if(I && HAS_BLOOD_DNA(I)) + if(I && GET_ATOM_BLOOD_DNA_LENGTH(I)) return FALSE return TRUE diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 2dd22bd7571..3447985e3be 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -487,7 +487,7 @@ if(!last_move || isspaceturf(oldLoc)) //if we didn't sucessfully move, or if our old location was a spaceturf. return var/obj/effect/decal/cleanable/blood/tracks/B = new(oldLoc) - B.add_blood_DNA(return_blood_DNA()) + B.add_blood_DNA(GET_ATOM_BLOOD_DNA(src)) B.setDir(direct) bloodiness-- diff --git a/modular_skyrat/modules/digi_bloodsole/code/_shoes.dm b/modular_skyrat/modules/digi_bloodsole/code/_shoes.dm index a25aae12cd3..9076ce503fd 100644 --- a/modular_skyrat/modules/digi_bloodsole/code/_shoes.dm +++ b/modular_skyrat/modules/digi_bloodsole/code/_shoes.dm @@ -4,7 +4,7 @@ return if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe") - if(HAS_BLOOD_DNA(src)) + if(GET_ATOM_BLOOD_DNA(src)) if (mutant_styles & CLOTHING_DIGITIGRADE_VARIATION) if(clothing_flags & LARGE_WORN_ICON) . += mutable_appearance('modular_skyrat/modules/digi_bloodsole/icons/64x64.dmi', "shoeblood_large_digi") diff --git a/tgstation.dme b/tgstation.dme index adcb7c22e1a..915a56ce9d4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -349,6 +349,7 @@ #include "code\__HELPERS\dna.dm" #include "code\__HELPERS\files.dm" #include "code\__HELPERS\filters.dm" +#include "code\__HELPERS\forensics.dm" #include "code\__HELPERS\game.dm" #include "code\__HELPERS\global_lists.dm" #include "code\__HELPERS\guid.dm" @@ -786,7 +787,6 @@ #include "code\datums\components\explodable.dm" #include "code\datums\components\faction_granter.dm" #include "code\datums\components\force_move.dm" -#include "code\datums\components\forensics.dm" #include "code\datums\components\fov_handler.dm" #include "code\datums\components\fullauto.dm" #include "code\datums\components\gas_leaker.dm" @@ -2757,7 +2757,6 @@ #include "code\modules\clothing\under\jobs\Plasmaman\engineering.dm" #include "code\modules\clothing\under\jobs\Plasmaman\medsci.dm" #include "code\modules\clothing\under\jobs\Plasmaman\security.dm" -#include "code\modules\detectivework\detective_work.dm" #include "code\modules\detectivework\evidence.dm" #include "code\modules\detectivework\footprints_and_rag.dm" #include "code\modules\detectivework\scanner.dm" @@ -2925,6 +2924,8 @@ #include "code\modules\food_and_drinks\restaurant\custom_order.dm" #include "code\modules\food_and_drinks\restaurant\generic_venues.dm" #include "code\modules\food_and_drinks\restaurant\customers\_customer.dm" +#include "code\modules\forensics\_forensics.dm" +#include "code\modules\forensics\forensics_helpers.dm" #include "code\modules\holiday\easter.dm" #include "code\modules\holiday\foreign_calendar.dm" #include "code\modules\holiday\holidays.dm"