[MIRROR] Refactors the forensics component into a datum [MDB IGNORE] (#13649)

* Refactors the forensics component into a datum

* Refactors the forensics component into a datum

* Refactors the forensics component into a datum

Co-authored-by: Seth Scherer <supernovaa41@gmx.com>
Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com>
This commit is contained in:
SkyratBot
2022-05-17 21:32:15 +02:00
committed by GitHub
parent 795df5dc0e
commit 55ec232462
28 changed files with 408 additions and 225 deletions

View File

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

View File

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

View File

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

View File

@@ -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()

View File

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

View File

@@ -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)])

View File

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

View File

@@ -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 ..()

View File

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

View File

@@ -3,7 +3,7 @@
return
var/interface = "A log of every player who has touched [victim], sorted by last touch.<br><br><ol>"
var/victim_hiddenprints = victim.return_hiddenprints()
var/victim_hiddenprints = GET_ATOM_HIDDENPRINTS(victim)
if(!islist(victim_hiddenprints))
victim_hiddenprints = list()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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" : ""]!")

View File

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

View File

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

View File

@@ -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")

View File

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