mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
[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:
@@ -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"
|
||||
|
||||
10
code/__HELPERS/forensics.dm
Normal file
10
code/__HELPERS/forensics.dm
Normal 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))
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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)])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
237
code/modules/forensics/_forensics.dm
Normal file
237
code/modules/forensics/_forensics.dm
Normal 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)
|
||||
122
code/modules/forensics/forensics_helpers.dm
Normal file
122
code/modules/forensics/forensics_helpers.dm
Normal 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))
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" : ""]!")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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--
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user