diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm new file mode 100644 index 0000000000..1950b8b664 --- /dev/null +++ b/code/datums/components/forensics.dm @@ -0,0 +1,158 @@ +/datum/component/forensics + 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 = fingerprints | F.fingerprints + hiddenprints = hiddenprints | F.hiddenprints + blood_DNA = blood_DNA | F.blood_DNA + fibers = fibers | F.fibers + check_blood() + return ..() + +/datum/component/forensics/Initialize(new_fingerprints, new_hiddenprints, new_blood_DNA, new_fibers) + if(!isatom(parent)) + . = COMPONENT_INCOMPATIBLE + CRASH("Forensics datum applied incorrectly to non-atom of type [parent.type]!") + fingerprints = new_fingerprints + hiddenprints = new_hiddenprints + blood_DNA = new_blood_DNA + fibers = new_fibers + check_blood() + RegisterSignal(COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_act) + +/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 + if(isitem(parent)) + qdel(parent.GetComponent(/datum/component/decal/blood)) + return TRUE + +/datum/component/forensics/proc/wipe_fibers() + fibers = null + return TRUE + +/datum/component/forensics/proc/clean_act(strength) + if(strength >= CLEAN_STRENGTH_FINGERPRINTS) + wipe_fingerprints() + if(strength >= CLEAN_STRENGTH_BLOOD) + wipe_blood_DNA() + if(strength >= CLEAN_STRENGTH_FIBERS) + wipe_fibers() + +/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(!M) + return + add_hiddenprint(M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + add_fibers(H) + if(H.gloves) //Check if the gloves (if any) hide fingerprints + var/obj/item/clothing/gloves/G = H.gloves + if(G.transfer_prints) + ignoregloves = TRUE + if(!ignoregloves) + H.gloves.add_fingerprint(H, TRUE) //ignoregloves = 1 to avoid infinite loop. + return + var/full_print = md5(H.dna.uni_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/living/M) + if(!M || !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: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]") + else + var/laststamppos = findtext(LAZYACCESS(hiddenprints, M.key), " Last: ") + if(laststamppos) + LAZYSET(hiddenprints, M.key, copytext(hiddenprints[M.key], 1, laststamppos)) + hiddenprints[M.key] += " Last: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]" //made sure to be existing by if(!LAZYACCESS);else + parent.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.LoadComponent(/datum/component/decal/blood)