mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
[MIRROR] Forensics Datum (#11015)
Co-authored-by: Will <7099514+Willburd@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
887639ef5c
commit
d7cd22d2d0
@@ -7,6 +7,7 @@
|
||||
#define get_z(A) (get_step(A, 0)?.z || 0)
|
||||
|
||||
#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
|
||||
#define DEFAULT_BLOOD_TYPE "A+"
|
||||
|
||||
// #define to_chat(target, message) target << message Not anymore!
|
||||
//#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat
|
||||
|
||||
@@ -375,7 +375,7 @@ GLOBAL_LIST_EMPTY(PDA_Manifest)
|
||||
M.fields["species"] = "[H.custom_species ? "[H.custom_species]" : H.species.name]"
|
||||
else
|
||||
M.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]"
|
||||
M.fields["b_type"] = H.b_type
|
||||
M.fields["b_type"] = H.dna ? H.dna.b_type : DEFAULT_BLOOD_TYPE
|
||||
M.fields["blood_reagent"] = H.dna.blood_reagents
|
||||
M.fields["blood_color"] = H.dna.blood_color
|
||||
M.fields["b_dna"] = H.dna.unique_enzymes
|
||||
@@ -414,7 +414,7 @@ GLOBAL_LIST_EMPTY(PDA_Manifest)
|
||||
L.fields["brain_type"] = H.get_FBP_type()
|
||||
else
|
||||
L.fields["brain_type"] = "Organic"
|
||||
L.fields["b_type"] = H.b_type
|
||||
L.fields["b_type"] = H.dna ? H.dna.b_type : DEFAULT_BLOOD_TYPE
|
||||
L.fields["b_dna"] = H.dna.unique_enzymes
|
||||
L.fields["enzymes"] = H.dna.SE // Used in respawning
|
||||
L.fields["identity"] = H.dna.UI // "
|
||||
|
||||
322
code/datums/forensics_crime.dm
Normal file
322
code/datums/forensics_crime.dm
Normal file
@@ -0,0 +1,322 @@
|
||||
/datum/forensics_crime
|
||||
VAR_PRIVATE/list/fingerprints = null
|
||||
VAR_PRIVATE/list/fingerprintshidden = null
|
||||
VAR_PRIVATE/list/suit_fibres = null
|
||||
VAR_PRIVATE/list/blood_DNA = null
|
||||
VAR_PRIVATE/fingerprintslast = null
|
||||
VAR_PRIVATE/gunshot_residue = null
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fingerprints
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Adds fingerprints to the object, prints can be smudged, glove handling is done on the atom side of this proc.
|
||||
/datum/forensics_crime/proc/add_prints(var/mob/living/carbon/human/H)
|
||||
//Now, lets get to the dirty work.
|
||||
if(!fingerprints)
|
||||
fingerprints = list()
|
||||
if(!fingerprintshidden)
|
||||
fingerprintshidden = list()
|
||||
|
||||
//More adminstuffz
|
||||
if(fingerprintslast != H.key)
|
||||
fingerprintshidden += "[time_stamp()]: [key_name(H)]"
|
||||
fingerprintslast = H.key
|
||||
|
||||
//Hash this shit.
|
||||
var/full_print = H.get_full_print()
|
||||
|
||||
// Add the fingerprints
|
||||
if(fingerprints[full_print])
|
||||
switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints.
|
||||
|
||||
if(28 to 32)
|
||||
if(prob(1))
|
||||
fingerprints[full_print] = full_print // You rolled a one buddy.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32
|
||||
|
||||
if(24 to 27)
|
||||
if(prob(3))
|
||||
fingerprints[full_print] = full_print //Sucks to be you.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29
|
||||
|
||||
if(20 to 23)
|
||||
if(prob(5))
|
||||
fingerprints[full_print] = full_print //Had a good run didn't ya.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25
|
||||
|
||||
if(16 to 19)
|
||||
if(prob(5))
|
||||
fingerprints[full_print] = full_print //Welp.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21
|
||||
|
||||
if(0 to 15)
|
||||
if(prob(5))
|
||||
fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge.
|
||||
else
|
||||
fingerprints[full_print] = full_print
|
||||
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time.
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Returns a list of fingerprint strings that have touched an object. Always returns a list.
|
||||
/datum/forensics_crime/proc/get_prints()
|
||||
RETURN_TYPE(/list)
|
||||
if(!fingerprints)
|
||||
return list()
|
||||
return fingerprints
|
||||
|
||||
/// Returns true if any fingerprints are present on this object.
|
||||
/datum/forensics_crime/proc/has_prints()
|
||||
if(!fingerprints || !fingerprints.len)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Merges data from another forensics crime datum into this one. Entries with the same key will be merged. Does nothing if the origin datum's list is empty.
|
||||
/datum/forensics_crime/proc/merge_prints(var/datum/forensics_crime/origin)
|
||||
if(!islist(origin?.fingerprints))
|
||||
return
|
||||
if(fingerprints)
|
||||
fingerprints |= origin.fingerprints
|
||||
else
|
||||
fingerprints = origin.fingerprints.Copy()
|
||||
|
||||
/// Clears data to default state, wiping all evidence
|
||||
/datum/forensics_crime/proc/clear_prints()
|
||||
LAZYCLEARLIST(fingerprints)
|
||||
fingerprintslast = null
|
||||
|
||||
/// Sets the key of the last mob to touch this object
|
||||
/datum/forensics_crime/proc/set_lastprint(var/val)
|
||||
fingerprintslast = val
|
||||
|
||||
/// Gets the key of the last mob to touch this object
|
||||
/datum/forensics_crime/proc/get_lastprint()
|
||||
return fingerprintslast
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Hidden prints
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Adds hidden admin trackable fingerprints, visible even if normal fingerprints are smudged.
|
||||
/datum/forensics_crime/proc/add_hiddenprints(mob/living/M as mob)
|
||||
if(!ishuman(M))
|
||||
if(fingerprintslast != M.key)
|
||||
fingerprintshidden += text("\[[time_stamp()]\] (Non-human mob). Real name: [], Key: []",M.real_name, M.key)
|
||||
fingerprintslast = M.key
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.gloves)
|
||||
if(fingerprintslast != H.key)
|
||||
fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key)
|
||||
fingerprintslast = H.key
|
||||
return FALSE
|
||||
if (mFingerprints in M.mutations)
|
||||
if(fingerprintslast != H.key)
|
||||
fingerprintshidden += text("\[[time_stamp()]\] (Noprint mutation). Real name: [], Key: []",H.real_name, H.key)
|
||||
fingerprintslast = H.key
|
||||
return FALSE
|
||||
if(fingerprints)
|
||||
return FALSE
|
||||
if(fingerprintslast != H.key)
|
||||
fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key)
|
||||
fingerprintslast = H.key
|
||||
return TRUE
|
||||
|
||||
/// Returns a list of hidden admin fingerprint strings that describe how a mob interacted with an object, for debugging forensics, or investigating player behavior. Always returns a list.
|
||||
/datum/forensics_crime/proc/get_hiddenprints()
|
||||
RETURN_TYPE(/list)
|
||||
if(!fingerprintshidden)
|
||||
return list()
|
||||
return fingerprintshidden
|
||||
|
||||
/// Returns true if and hidden admin fingerprints are on this object
|
||||
/datum/forensics_crime/proc/has_hiddenprints()
|
||||
if(!fingerprintshidden || !fingerprintshidden.len)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Merges data from another forensics crime datum into this one. Entries with the same key will be merged. Does nothing if the origin datum's list is empty.
|
||||
/datum/forensics_crime/proc/merge_hiddenprints(var/datum/forensics_crime/origin)
|
||||
if(!islist(origin?.fingerprintshidden))
|
||||
return
|
||||
if(fingerprintshidden)
|
||||
fingerprintshidden |= origin.fingerprintshidden
|
||||
else
|
||||
fingerprintshidden = origin.fingerprintshidden.Copy()
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fibres
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Adds stray fibres from clothing worn by a mob while handling something
|
||||
/datum/forensics_crime/proc/add_fibres(var/mob/living/carbon/human/M)
|
||||
var/fibertext = null
|
||||
var/item_multiplier = istype(src,/obj/item)?1.2:1
|
||||
var/suit_coverage = 0
|
||||
if(M.wear_suit)
|
||||
if(prob(10*item_multiplier))
|
||||
fibertext = "Material from \a [M.wear_suit]."
|
||||
suit_coverage = M.wear_suit.body_parts_covered
|
||||
|
||||
if(M.w_uniform && (M.w_uniform.body_parts_covered & ~suit_coverage))
|
||||
if(prob(15*item_multiplier))
|
||||
fibertext = "Fibers from \a [M.w_uniform]."
|
||||
|
||||
if(M.gloves && (M.gloves.body_parts_covered & ~suit_coverage))
|
||||
if(prob(20*item_multiplier))
|
||||
fibertext = "Material from a pair of [M.gloves.name]."
|
||||
|
||||
if(!fibertext)
|
||||
return
|
||||
|
||||
// Add to dataset
|
||||
if(suit_fibres)
|
||||
if(!(fibertext in suit_fibres))
|
||||
suit_fibres.Add(fibertext)
|
||||
return
|
||||
suit_fibres = list()
|
||||
suit_fibres.Add(fibertext)
|
||||
|
||||
/// Gets a list of fibres contaminating this object
|
||||
/datum/forensics_crime/proc/get_fibres()
|
||||
RETURN_TYPE(/list)
|
||||
if(!suit_fibres)
|
||||
return list()
|
||||
return suit_fibres
|
||||
|
||||
/// Returns true if any stray clothing fibres are on this object
|
||||
/datum/forensics_crime/proc/has_fibres()
|
||||
if(!suit_fibres || !suit_fibres.len)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Merges data from another forensics crime datum into this one. Entries with the same key will be merged. Does nothing if the origin datum's list is empty.
|
||||
/datum/forensics_crime/proc/merge_fibres(var/datum/forensics_crime/origin)
|
||||
if(!islist(origin?.suit_fibres))
|
||||
return
|
||||
if(suit_fibres)
|
||||
suit_fibres |= origin.suit_fibres
|
||||
else
|
||||
suit_fibres = origin.suit_fibres.Copy()
|
||||
|
||||
/// Clears data to default state, wiping all evidence
|
||||
/datum/forensics_crime/proc/clear_fibres()
|
||||
LAZYCLEARLIST(suit_fibres)
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Blood dna
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
#define XENO_DNA "UNKNOWN DNA STRUCTURE"
|
||||
#define NOT_HUMAN_DNA "Non-human DNA"
|
||||
/// Adds the mob's bloodtype to a UE keyed list, returns true if the key was not present in the list before.
|
||||
/datum/forensics_crime/proc/add_blooddna(var/datum/dna/dna_data,var/mob/M)
|
||||
if(!blood_DNA)
|
||||
blood_DNA = list()
|
||||
// Special alien handling
|
||||
if(istype(M, /mob/living/carbon/alien))
|
||||
var/fresh = isnull(blood_DNA[XENO_DNA])
|
||||
blood_DNA[XENO_DNA] = "X*"
|
||||
return fresh
|
||||
// Simple mob gibbing
|
||||
if(!dna_data)
|
||||
var/fresh = isnull(blood_DNA[NOT_HUMAN_DNA])
|
||||
blood_DNA[NOT_HUMAN_DNA] = DEFAULT_BLOOD_TYPE
|
||||
return fresh
|
||||
// Standard blood
|
||||
var/fresh = isnull(blood_DNA[dna_data.unique_enzymes])
|
||||
blood_DNA[dna_data.unique_enzymes] = dna_data.b_type
|
||||
return fresh
|
||||
#undef XENO_DNA
|
||||
#undef NOT_HUMAN_DNA
|
||||
|
||||
/// Adds the mob's bloodtype to a UE keyed list, returns true if the key was not present in the list before. Uses an organ's dna_data datum instead of a mob's dna datum.
|
||||
/datum/forensics_crime/proc/add_blooddna_organ(var/datum/organ_data/dna_data)
|
||||
if(!blood_DNA)
|
||||
blood_DNA = list()
|
||||
var/fresh = isnull(blood_DNA[dna_data.unique_enzymes])
|
||||
blood_DNA[dna_data.unique_enzymes] = dna_data.b_type
|
||||
return fresh
|
||||
|
||||
/// Returns a list of UE keys with bloodtype values that have contaminated this object. Always returns a list.
|
||||
/datum/forensics_crime/proc/get_blooddna()
|
||||
RETURN_TYPE(/list)
|
||||
if(!blood_DNA)
|
||||
return list()
|
||||
return blood_DNA
|
||||
|
||||
/// Returns true if any blood contaminated this object
|
||||
/datum/forensics_crime/proc/has_blooddna()
|
||||
if(!blood_DNA || !blood_DNA.len)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Merges data from another forensics crime datum into this one. Entries with the same key will be merged. Does nothing if the origin datum's list is empty. Supports merging from a list directly as well.
|
||||
/datum/forensics_crime/proc/merge_blooddna(var/datum/forensics_crime/origin, var/list/raw_list = null)
|
||||
// Copying from a list, blood on a mob's feet is stored as a list outside of forensics data
|
||||
if(raw_list)
|
||||
if(blood_DNA)
|
||||
blood_DNA |= raw_list
|
||||
else
|
||||
blood_DNA = raw_list.Copy()
|
||||
return
|
||||
// Copying from another datums
|
||||
if(!islist(origin?.blood_DNA))
|
||||
return
|
||||
if(blood_DNA)
|
||||
blood_DNA |= origin.blood_DNA
|
||||
else
|
||||
blood_DNA = origin.blood_DNA.Copy()
|
||||
|
||||
/// Clears data to default state, wiping all evidence
|
||||
/datum/forensics_crime/proc/clear_blooddna()
|
||||
LAZYCLEARLIST(blood_DNA)
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Gunshot residue
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Sets a string name of the last fired bullet's caliber from a projectile based gun.
|
||||
/datum/forensics_crime/proc/add_gunshotresidue(var/gsr)
|
||||
gunshot_residue = gsr
|
||||
|
||||
/// Gets a string name of the last bullet caliber fired from a projectile based gun.
|
||||
/datum/forensics_crime/proc/get_gunshotresidue()
|
||||
return gunshot_residue
|
||||
|
||||
/// Clears data to default state, wiping all evidence
|
||||
/datum/forensics_crime/proc/clear_gunshotresidue()
|
||||
gunshot_residue = null
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Misc procs
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Cleans off forensic information, different cleaning types remove different things.
|
||||
/datum/forensics_crime/proc/wash(var/clean_types)
|
||||
// These require specific cleaning flags
|
||||
if(clean_types & CLEAN_TYPE_BLOOD)
|
||||
clear_blooddna()
|
||||
if(clean_types & CLEAN_TYPE_FINGERPRINTS)
|
||||
clear_prints()
|
||||
if(clean_types & CLEAN_TYPE_FIBERS)
|
||||
clear_fibres()
|
||||
// anything will wash away gunshot residue
|
||||
if(clean_types > 0)
|
||||
clear_gunshotresidue()
|
||||
|
||||
/// Merges both visible and admin hidden investigation fingerprints, as well as setting the last fingerprint from the origin datum's if one is not already set.
|
||||
/datum/forensics_crime/proc/merge_allprints(var/datum/forensics_crime/origin)
|
||||
if(!origin)
|
||||
return
|
||||
merge_prints(origin)
|
||||
merge_hiddenprints(origin)
|
||||
if(origin.fingerprintslast)
|
||||
fingerprintslast = origin.fingerprintslast
|
||||
@@ -2,10 +2,6 @@
|
||||
layer = TURF_LAYER //This was here when I got here. Why though?
|
||||
var/level = 2
|
||||
var/flags = 0
|
||||
var/list/fingerprints
|
||||
var/list/fingerprintshidden
|
||||
var/fingerprintslast = null
|
||||
var/list/blood_DNA
|
||||
var/was_bloodied
|
||||
var/blood_color
|
||||
var/pass_flags = 0
|
||||
@@ -14,6 +10,7 @@
|
||||
var/simulated = TRUE //filter for actions - used by lighting overlays
|
||||
var/atom_say_verb = "says"
|
||||
var/bubble_icon = "normal" ///what icon the atom uses for speechbubbles
|
||||
var/datum/forensics_crime/forensic_data
|
||||
var/fluorescent // Shows up under a UV light.
|
||||
|
||||
var/last_bumped = 0
|
||||
@@ -65,6 +62,8 @@
|
||||
QDEL_NULL(reagents)
|
||||
if(light)
|
||||
QDEL_NULL(light)
|
||||
if(forensic_data)
|
||||
QDEL_NULL(forensic_data)
|
||||
return ..()
|
||||
|
||||
/atom/proc/reveal_blood()
|
||||
@@ -187,7 +186,7 @@
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
//This reformat names to get a/an properly working on item descriptions when they are bloody
|
||||
var/f_name = "\a [src][infix]."
|
||||
if(src.blood_DNA && !istype(src, /obj/effect/decal))
|
||||
if(forensic_data?.has_blooddna() && !istype(src, /obj/effect/decal))
|
||||
if(gender == PLURAL)
|
||||
f_name = "some "
|
||||
else
|
||||
@@ -270,163 +269,12 @@
|
||||
AM.throwing = 0
|
||||
return
|
||||
|
||||
/atom/proc/add_hiddenprint(mob/living/M as mob)
|
||||
if(isnull(M)) return
|
||||
if(isnull(M.key)) return
|
||||
if (ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if (!istype(H.dna, /datum/dna))
|
||||
return 0
|
||||
if (H.gloves)
|
||||
if(src.fingerprintslast != H.key)
|
||||
src.fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key)
|
||||
src.fingerprintslast = H.key
|
||||
return 0
|
||||
if (!( src.fingerprints ))
|
||||
if(src.fingerprintslast != H.key)
|
||||
src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key)
|
||||
src.fingerprintslast = H.key
|
||||
return 1
|
||||
else
|
||||
if(src.fingerprintslast != M.key)
|
||||
src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key)
|
||||
src.fingerprintslast = M.key
|
||||
return
|
||||
|
||||
/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0)
|
||||
if(isnull(M)) return
|
||||
if(isAI(M)) return
|
||||
if(isnull(M.key)) return
|
||||
if (ishuman(M))
|
||||
//Add the list if it does not exist.
|
||||
if(!fingerprintshidden)
|
||||
fingerprintshidden = list()
|
||||
|
||||
//Fibers~
|
||||
add_fibers(M)
|
||||
|
||||
//He has no prints!
|
||||
if (mFingerprints in M.mutations)
|
||||
if(fingerprintslast != M.key)
|
||||
fingerprintshidden += "[time_stamp()]: [key_name(M)] (No fingerprints mutation)"
|
||||
fingerprintslast = M.key
|
||||
return 0 //Now, lets get to the dirty work.
|
||||
//First, make sure their DNA makes sense.
|
||||
var/mob/living/carbon/human/H = M
|
||||
if (!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32))
|
||||
if(!istype(H.dna, /datum/dna))
|
||||
H.dna = new /datum/dna(null)
|
||||
H.dna.real_name = H.real_name
|
||||
H.check_dna()
|
||||
|
||||
//Now, deal with gloves.
|
||||
if (H.gloves && H.gloves != src)
|
||||
if(fingerprintslast != H.key)
|
||||
fingerprintshidden += "[time_stamp()]: [key_name(H)] (Wearing [H.gloves])"
|
||||
fingerprintslast = H.key
|
||||
H.gloves.add_fingerprint(M)
|
||||
|
||||
//Deal with gloves the pass finger/palm prints.
|
||||
if(!ignoregloves)
|
||||
if(H.gloves && H.gloves != src)
|
||||
if(istype(H.gloves, /obj/item/clothing/gloves))
|
||||
var/obj/item/clothing/gloves/G = H.gloves
|
||||
if(!prob(G.fingerprint_chance))
|
||||
return 0
|
||||
|
||||
//More adminstuffz
|
||||
if(fingerprintslast != H.key)
|
||||
fingerprintshidden += "[time_stamp()]: [key_name(H)]"
|
||||
fingerprintslast = H.key
|
||||
|
||||
//Make the list if it does not exist.
|
||||
if(!fingerprints)
|
||||
fingerprints = list()
|
||||
|
||||
//Hash this shit.
|
||||
var/full_print = H.get_full_print()
|
||||
|
||||
// Add the fingerprints
|
||||
//
|
||||
if(fingerprints[full_print])
|
||||
switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints.
|
||||
|
||||
if(28 to 32)
|
||||
if(prob(1))
|
||||
fingerprints[full_print] = full_print // You rolled a one buddy.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32
|
||||
|
||||
if(24 to 27)
|
||||
if(prob(3))
|
||||
fingerprints[full_print] = full_print //Sucks to be you.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29
|
||||
|
||||
if(20 to 23)
|
||||
if(prob(5))
|
||||
fingerprints[full_print] = full_print //Had a good run didn't ya.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25
|
||||
|
||||
if(16 to 19)
|
||||
if(prob(5))
|
||||
fingerprints[full_print] = full_print //Welp.
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21
|
||||
|
||||
if(0 to 15)
|
||||
if(prob(5))
|
||||
fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge.
|
||||
else
|
||||
fingerprints[full_print] = full_print
|
||||
|
||||
else
|
||||
fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time.
|
||||
|
||||
|
||||
return 1
|
||||
else
|
||||
//Smudge up dem prints some
|
||||
if(fingerprintslast != M.key)
|
||||
fingerprintshidden += "[time_stamp()]: [key_name(M)]"
|
||||
fingerprintslast = M.key
|
||||
|
||||
//Cleaning up shit.
|
||||
if(fingerprints && !fingerprints.len)
|
||||
qdel(fingerprints)
|
||||
return
|
||||
|
||||
|
||||
/atom/proc/transfer_fingerprints_to(var/atom/A)
|
||||
|
||||
if(!istype(A.fingerprints,/list))
|
||||
A.fingerprints = list()
|
||||
|
||||
if(!istype(A.fingerprintshidden,/list))
|
||||
A.fingerprintshidden = list()
|
||||
|
||||
if(!istype(fingerprintshidden, /list))
|
||||
fingerprintshidden = list()
|
||||
|
||||
//skytodo
|
||||
//A.fingerprints |= fingerprints //detective
|
||||
//A.fingerprintshidden |= fingerprintshidden //admin
|
||||
if(A.fingerprints && fingerprints)
|
||||
A.fingerprints |= fingerprints.Copy() //detective
|
||||
if(A.fingerprintshidden && fingerprintshidden)
|
||||
A.fingerprintshidden |= fingerprintshidden.Copy() //admin A.fingerprintslast = fingerprintslast
|
||||
|
||||
|
||||
//returns 1 if made bloody, returns 0 otherwise
|
||||
/atom/proc/add_blood(mob/living/carbon/human/M as mob)
|
||||
|
||||
if(flags & NOBLOODY)
|
||||
return 0
|
||||
|
||||
if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it.
|
||||
blood_DNA = list()
|
||||
|
||||
was_bloodied = TRUE
|
||||
if(!blood_color)
|
||||
blood_color = SYNTH_BLOOD_COLOUR
|
||||
@@ -816,10 +664,7 @@ GLOBAL_LIST_EMPTY(icon_dimensions)
|
||||
remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
|
||||
return TRUE
|
||||
|
||||
if(istype(blood_DNA, /list))
|
||||
blood_DNA = null
|
||||
return TRUE
|
||||
|
||||
forensic_data?.wash(clean_types)
|
||||
blood_color = null
|
||||
germ_level = 0
|
||||
fluorescent = 0
|
||||
|
||||
@@ -46,7 +46,7 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
|
||||
var/list/UI[DNA_UI_LENGTH]
|
||||
|
||||
// From old dna.
|
||||
var/b_type = "A+" // Should probably change to an integer => string map but I'm lazy.
|
||||
var/b_type = DEFAULT_BLOOD_TYPE // Should probably change to an integer => string map but I'm lazy.
|
||||
var/real_name // Stores the real name of the person who originally got this dna datum. Used primarily for changelings,
|
||||
|
||||
// VOREStation
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
src.dna.b_type = "AB+" //This is needed to avoid blood rejection bugs. The fact that the blood type might not match up w/ records could be a *FEATURE* too.
|
||||
if(ishuman(src))
|
||||
var/mob/living/carbon/human/H = src
|
||||
H.b_type = "AB+" //For some reason we have two blood types on the mob.
|
||||
H.identifying_gender = chosen_dna.identifying_gender
|
||||
H.flavor_texts = chosen_dna.flavour_texts ? chosen_dna.flavour_texts.Copy() : null
|
||||
src.real_name = chosen_dna.name
|
||||
|
||||
@@ -417,8 +417,7 @@ GLOBAL_LIST_INIT(rnwords, list("ire","ego","nahlizet","certum","veri","jatkaa","
|
||||
R.word2 = english[required[2]]
|
||||
R.word3 = english[required[3]]
|
||||
R.check_icon()
|
||||
R.blood_DNA = list()
|
||||
R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type
|
||||
R.add_blooddna(H.dna,H)
|
||||
return
|
||||
else
|
||||
to_chat(user, "The book seems full of illegible scribbles. Is this a joke?")
|
||||
@@ -451,8 +450,7 @@ GLOBAL_LIST_INIT(rnwords, list("ire","ego","nahlizet","certum","veri","jatkaa","
|
||||
var/obj/effect/rune/R = new /obj/effect/rune
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
R.blood_DNA = list()
|
||||
R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type
|
||||
R.add_blooddna(H.dna,H)
|
||||
var/area/A = get_area(user)
|
||||
log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].")
|
||||
switch(r)
|
||||
|
||||
@@ -1038,7 +1038,7 @@ var/list/sacrificed = list()
|
||||
culcount++
|
||||
if(culcount >= 5)
|
||||
for(var/obj/effect/rune/R in rune_list)
|
||||
if(R.blood_DNA == src.blood_DNA)
|
||||
if(R.forensic_data?.get_blooddna() == src.forensic_data?.get_blooddna())
|
||||
for(var/mob/living/M in orange(2,R))
|
||||
M.take_overall_damage(0,15)
|
||||
if (R.invisibility>M.see_invisible)
|
||||
@@ -1048,7 +1048,7 @@ var/list/sacrificed = list()
|
||||
var/turf/T = get_turf(R)
|
||||
T.hotspot_expose(700,125)
|
||||
for(var/obj/effect/decal/cleanable/blood/B in world)
|
||||
if(B.blood_DNA == src.blood_DNA)
|
||||
if(B.forensic_data?.get_blooddna() == src.forensic_data?.get_blooddna())
|
||||
for(var/mob/living/M in orange(1,B))
|
||||
M.take_overall_damage(0,5)
|
||||
to_chat(M, span_danger("Blood suddenly ignites, burning you!"))
|
||||
|
||||
@@ -45,9 +45,7 @@
|
||||
return
|
||||
|
||||
var/obj/machinery/M = new build_machine_type(get_turf(src.loc), ndir, 1, frame_type)
|
||||
M.fingerprints = fingerprints
|
||||
M.fingerprintshidden = fingerprintshidden
|
||||
M.fingerprintslast = fingerprintslast
|
||||
M.init_forensic_data().merge_allprints(forensic_data)
|
||||
if(istype(src.loc, /obj/item/gripper)) //Typical gripper shenanigans
|
||||
user.drop_item()
|
||||
qdel(src)
|
||||
@@ -94,9 +92,7 @@
|
||||
new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size))
|
||||
|
||||
var/obj/machinery/M = new build_machine_type(loc, ndir, 1, frame_type)
|
||||
M.fingerprints = fingerprints
|
||||
M.fingerprintshidden = fingerprintshidden
|
||||
M.fingerprintslast = fingerprintslast
|
||||
M.init_forensic_data().merge_allprints(forensic_data)
|
||||
if(istype(src.loc, /obj/item/gripper)) //Typical gripper shenanigans
|
||||
user.drop_item()
|
||||
qdel(src)
|
||||
|
||||
@@ -93,13 +93,14 @@
|
||||
var/whereLink = "<A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(show_log)
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/print_name = carry.my_atom.forensic_data?.get_lastprint()
|
||||
if(print_name)
|
||||
var/mob/M = get_mob_by_key(print_name)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [print_name][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [print_name].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
@@ -16,7 +16,6 @@ var/global/list/image/splatter_cache=list()
|
||||
icon_state = "mfloor1"
|
||||
random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7")
|
||||
var/base_icon = 'icons/effects/blood.dmi'
|
||||
blood_DNA = list()
|
||||
var/basecolor="#A10808" // Color when wet.
|
||||
var/synthblood = 0
|
||||
var/list/datum/disease/viruses = list()
|
||||
@@ -49,8 +48,7 @@ var/global/list/image/splatter_cache=list()
|
||||
if(src.loc && isturf(src.loc))
|
||||
for(var/obj/effect/decal/cleanable/blood/B in src.loc)
|
||||
if(B != src)
|
||||
if (B.blood_DNA)
|
||||
blood_DNA |= B.blood_DNA.Copy()
|
||||
init_forensic_data().merge_blooddna(B.forensic_data)
|
||||
if(!(B.flags & ATOM_INITIALIZED))
|
||||
B.delete_me = TRUE
|
||||
else
|
||||
@@ -102,21 +100,20 @@ var/global/list/image/splatter_cache=list()
|
||||
S.update_icon() // Cut previous overlays
|
||||
if(!S.blood_overlay)
|
||||
S.generate_blood_overlay()
|
||||
if(!S.blood_DNA)
|
||||
S.blood_DNA = list()
|
||||
if(!forensic_data?.has_blooddna())
|
||||
S.blood_overlay.color = basecolor
|
||||
S.add_overlay(S.blood_overlay)
|
||||
if(S.blood_overlay && S.blood_overlay.color != basecolor)
|
||||
S.blood_overlay.color = basecolor
|
||||
S.add_overlay(S.blood_overlay)
|
||||
S.blood_DNA |= blood_DNA.Copy()
|
||||
transfer_blooddna_to(S)
|
||||
perp.update_inv_shoes()
|
||||
|
||||
else if (hasfeet)//Or feet
|
||||
perp.feet_blood_color = basecolor
|
||||
perp.track_blood = max(amount,perp.track_blood)
|
||||
LAZYINITLIST(perp.feet_blood_DNA)
|
||||
perp.feet_blood_DNA |= blood_DNA.Copy()
|
||||
perp.feet_blood_DNA |= init_forensic_data().get_blooddna().Copy()
|
||||
perp.update_bloodied()
|
||||
else if (perp.buckled && istype(perp.buckled, /obj/structure/bed/chair/wheelchair))
|
||||
var/obj/structure/bed/chair/wheelchair/W = perp.buckled
|
||||
@@ -145,9 +142,7 @@ var/global/list/image/splatter_cache=list()
|
||||
var/taken = rand(1,amount)
|
||||
amount -= taken
|
||||
to_chat(user, span_notice("You get some of \the [src] on your hands."))
|
||||
if (!user.blood_DNA)
|
||||
user.blood_DNA = list()
|
||||
user.blood_DNA |= blood_DNA.Copy()
|
||||
transfer_blooddna_to(user)
|
||||
user.bloody_hands += taken
|
||||
user.hand_blood_color = basecolor
|
||||
user.update_inv_gloves(1)
|
||||
|
||||
@@ -113,8 +113,7 @@ var/global/list/image/fluidtrack_cache=list()
|
||||
updated=1
|
||||
|
||||
dirs |= comingdir|realgoing
|
||||
if(islist(blood_DNA))
|
||||
blood_DNA |= DNA.Copy()
|
||||
init_forensic_data().merge_blooddna(null,DNA)
|
||||
if(updated)
|
||||
update_icon()
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@
|
||||
|
||||
gib.update_icon()
|
||||
|
||||
gib.blood_DNA = list()
|
||||
gib.init_forensic_data()
|
||||
if(MobDNA)
|
||||
gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type
|
||||
gib.add_blooddna(MobDNA,null)
|
||||
else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey
|
||||
gib.blood_DNA["Non-human DNA"] = "A+"
|
||||
gib.add_blooddna(null,null)
|
||||
if(istype(location,/turf/))
|
||||
var/list/directions = gibdirections[i]
|
||||
if(directions.len)
|
||||
|
||||
@@ -736,12 +736,9 @@ var/list/global/slot_flags_enumeration = list(
|
||||
//Make the blood_overlay have the proper color then apply it.
|
||||
blood_overlay.color = blood_color
|
||||
add_overlay(blood_overlay)
|
||||
|
||||
//if this blood isn't already in the list, add it
|
||||
if(istype(M))
|
||||
if(blood_DNA[M.dna.unique_enzymes])
|
||||
return 0 //already bloodied with this blood. Cannot add more.
|
||||
blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
|
||||
add_blooddna(M.dna,M)
|
||||
|
||||
return 1 //we applied blood to the item
|
||||
|
||||
GLOBAL_LIST_EMPTY(blood_overlays_by_type)
|
||||
|
||||
@@ -340,7 +340,7 @@
|
||||
internal_data["stat_display_active2"] = data2
|
||||
if(loc)
|
||||
var/obj/item/PDA = loc
|
||||
var/mob/user = PDA.fingerprintslast
|
||||
var/mob/user = PDA.forensic_data?.get_lastprint()
|
||||
log_admin("STATUS: [user] set status screen with [src]. Message: [data1] [data2]")
|
||||
message_admins("STATUS: [user] set status screen with [src]. Message: [data1] [data2]")
|
||||
|
||||
|
||||
@@ -200,12 +200,12 @@
|
||||
if(attacher)
|
||||
log_str += ADMIN_QUE(attacher)
|
||||
|
||||
var/mob/mob = get_mob_by_key(src.fingerprintslast)
|
||||
var/mob/mob = get_mob_by_key(forensic_data?.get_lastprint())
|
||||
var/last_touch_info = ""
|
||||
if(mob)
|
||||
last_touch_info = ADMIN_QUE(mob)
|
||||
|
||||
log_str += " Last touched by: [src.fingerprintslast][last_touch_info]"
|
||||
log_str += " Last touched by: [forensic_data?.get_lastprint()][last_touch_info]"
|
||||
GLOB.bombers += log_str
|
||||
message_admins(log_str, 0, 1)
|
||||
log_game(log_str)
|
||||
|
||||
@@ -334,11 +334,7 @@
|
||||
S.add(transfer)
|
||||
if (prob(transfer/orig_amount * 100))
|
||||
transfer_fingerprints_to(S)
|
||||
if(blood_DNA)
|
||||
if(S.blood_DNA)
|
||||
S.blood_DNA |= blood_DNA
|
||||
else
|
||||
S.blood_DNA = blood_DNA.Copy()
|
||||
transfer_blooddna_to(S)
|
||||
return transfer
|
||||
return 0
|
||||
|
||||
@@ -361,8 +357,7 @@
|
||||
newstack.color = color
|
||||
if (prob(transfer/orig_amount * 100))
|
||||
transfer_fingerprints_to(newstack)
|
||||
if(blood_DNA)
|
||||
newstack.blood_DNA |= blood_DNA
|
||||
transfer_blooddna_to(newstack)
|
||||
return newstack
|
||||
return null
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
if(pulledby)
|
||||
pulledby.start_pulling(S)
|
||||
transfer_fingerprints_to(S)
|
||||
if(blood_DNA)
|
||||
S.blood_DNA |= blood_DNA
|
||||
S.init_forensic_data().merge_blooddna(forensic_data)
|
||||
use(transfer)
|
||||
S.add(transfer)
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
|
||||
add_fingerprint(user)
|
||||
|
||||
if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any
|
||||
if(blood_overlay && forensic_data?.has_blooddna()) //updates blood overlay, if any
|
||||
cut_overlays()
|
||||
|
||||
var/icon/I = new /icon(src.icon, src.icon_state)
|
||||
|
||||
@@ -395,8 +395,8 @@ var/list/global/tank_gauge_cache = list()
|
||||
if(pressure > TANK_FRAGMENT_PRESSURE)
|
||||
if(integrity <= 7)
|
||||
if(!istype(src.loc,/obj/item/transfer_valve))
|
||||
message_admins("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].")
|
||||
log_game("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].")
|
||||
message_admins("Explosive tank rupture! last key to touch the tank was [forensic_data?.get_lastprint()].")
|
||||
log_game("Explosive tank rupture! last key to touch the tank was [forensic_data?.get_lastprint()].")
|
||||
|
||||
//Give the gas a chance to build up more pressure through reacting
|
||||
air_contents.react()
|
||||
|
||||
@@ -546,7 +546,6 @@
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.gunshot_residue = null
|
||||
if(H.gloves)
|
||||
H.gloves.wash(CLEAN_SCRUB)
|
||||
H.update_inv_gloves()
|
||||
@@ -559,7 +558,7 @@
|
||||
H.bloody_hands = 0
|
||||
H.germ_level = 0
|
||||
H.hand_blood_color = null
|
||||
LAZYCLEARLIST(H.blood_DNA)
|
||||
H.forensic_data?.wash(CLEAN_SCRUB)
|
||||
H.update_bloodied()
|
||||
else
|
||||
user.wash(CLEAN_SCRUB)
|
||||
|
||||
@@ -118,8 +118,8 @@
|
||||
var/obj/item/clothing/shoes/S = H.shoes
|
||||
if(istype(S))
|
||||
S.handle_movement(src,(H.m_intent == I_RUN ? 1 : 0), H) // handle_movement now needs to know who is moving, for inshoe steppies
|
||||
if(S.track_blood && S.blood_DNA)
|
||||
bloodDNA = S.blood_DNA
|
||||
if(S.track_blood && S.forensic_data?.has_blooddna())
|
||||
bloodDNA = S.forensic_data.get_blooddna()
|
||||
bloodcolor=S.blood_color
|
||||
S.track_blood--
|
||||
else
|
||||
@@ -193,12 +193,9 @@
|
||||
|
||||
if(istype(M))
|
||||
for(var/obj/effect/decal/cleanable/blood/B in contents)
|
||||
if(!B.blood_DNA)
|
||||
B.blood_DNA = list()
|
||||
if(!B.blood_DNA[M.dna.unique_enzymes])
|
||||
B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
|
||||
if(M.IsInfected())
|
||||
B.viruses = M.GetViruses()
|
||||
var/fresh = B.init_forensic_data().add_blooddna(M.dna,M)
|
||||
if(fresh && M.IsInfected())
|
||||
B.viruses = M.GetViruses()
|
||||
return 1 //we bloodied the floor
|
||||
blood_splatter(src,M.get_blood(M.vessel),1)
|
||||
return 1 //we bloodied the floor
|
||||
@@ -208,7 +205,7 @@
|
||||
/turf/simulated/proc/add_blood_floor(mob/living/carbon/M as mob)
|
||||
if( istype(M, /mob/living/carbon/alien ))
|
||||
var/obj/effect/decal/cleanable/blood/xeno/this = new /obj/effect/decal/cleanable/blood/xeno(src)
|
||||
this.blood_DNA["UNKNOWN BLOOD"] = "X*"
|
||||
this.init_forensic_data().add_blooddna(M.dna,M)
|
||||
else if( istype(M, /mob/living/silicon/robot ))
|
||||
new /obj/effect/decal/cleanable/blood/oil(src)
|
||||
else if(ishuman(M))
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
dat += "<table cellspacing=5><tr><th>Name</th><th>DNA</th><th>Blood Type</th></tr>"
|
||||
for(var/mob/living/carbon/human/H in mob_list)
|
||||
if(H.dna && H.ckey)
|
||||
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.b_type]</td></tr>"
|
||||
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.dna ? H.dna.b_type : DEFAULT_BLOOD_TYPE]</td></tr>"
|
||||
dat += "</table>"
|
||||
user << browse("<html>[dat]</html>", "window=DNA;size=440x410")
|
||||
|
||||
@@ -175,7 +175,7 @@ var/const/preview_icons = 'icons/mob/human_races/preview.dmi'
|
||||
character.h_style = pref.h_style
|
||||
character.f_style = pref.f_style
|
||||
character.grad_style= pref.grad_style
|
||||
character.b_type = pref.b_type
|
||||
character.dna.b_type= pref.b_type
|
||||
character.synth_color = pref.synth_color
|
||||
character.synth_markings = pref.synth_markings
|
||||
character.digitigrade = pref.digitigrade
|
||||
|
||||
@@ -22,7 +22,7 @@ var/list/preferences_datums = list()
|
||||
//character preferences
|
||||
var/real_name //our character's name
|
||||
var/nickname //our character's nickname
|
||||
var/b_type = "A+" //blood type (not-chooseable)
|
||||
var/b_type = DEFAULT_BLOOD_TYPE //blood type (not-chooseable)
|
||||
var/blood_reagents = "default" //blood restoration reagents
|
||||
var/headset = 1 //headset type
|
||||
var/backbag = 2 //backpack type
|
||||
@@ -456,7 +456,7 @@ var/list/preferences_datums = list()
|
||||
character.grad_style= grad_style
|
||||
character.f_style = f_style
|
||||
character.grad_style= grad_style
|
||||
character.b_type = b_type
|
||||
character.dna.b_type= b_type
|
||||
character.synth_color = synth_color
|
||||
|
||||
var/datum/preference/color/synth_color_color = GLOB.preference_entries[/datum/preference/color/human/synth_color]
|
||||
|
||||
@@ -224,7 +224,7 @@ badges
|
||||
if(!istype(H))
|
||||
return
|
||||
var/religion = "Unset"
|
||||
desc = "[initial(desc)]\nName: [H.real_name] ([H.get_species()])\nReligion: [religion]\nBlood type: [H.b_type]"
|
||||
desc = "[initial(desc)]\nName: [H.real_name] ([H.get_species()])\nReligion: [religion]\nBlood type: [H.dna ? H.dna.b_type : DEFAULT_BLOOD_TYPE]"
|
||||
|
||||
/obj/item/clothing/accessory/badge/solgov/representative
|
||||
name = "representative's badge"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
drop_sound = 'sound/items/drop/clothing.ogg'
|
||||
pickup_sound = 'sound/items/pickup/clothing.ogg'
|
||||
var/list/species_restricted = null //Only these species can wear this kit.
|
||||
var/gunshot_residue //Used by forensics.
|
||||
|
||||
var/list/accessories
|
||||
var/list/valid_accessory_slots
|
||||
@@ -32,12 +31,6 @@
|
||||
/obj/item/clothing/proc/update_clothing_icon()
|
||||
return
|
||||
|
||||
// Aurora forensics port.
|
||||
/obj/item/clothing/wash()
|
||||
. = ..()
|
||||
gunshot_residue = null
|
||||
|
||||
|
||||
/obj/item/clothing/Initialize(mapload)
|
||||
. = ..()
|
||||
if(starting_accessories)
|
||||
@@ -53,7 +46,7 @@
|
||||
|
||||
/obj/item/clothing/update_icon()
|
||||
cut_overlays() //This removes all the overlays on the sprite and then goes down a checklist adding them as required.
|
||||
if(blood_DNA)
|
||||
if(forensic_data?.has_blooddna())
|
||||
add_blood()
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
standing.add_overlay(A.get_mob_overlay())
|
||||
|
||||
/obj/item/clothing/apply_blood(var/image/standing)
|
||||
if(blood_DNA && blood_sprite_state && ishuman(loc))
|
||||
if(forensic_data?.has_blooddna() && blood_sprite_state && ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
var/image/bloodsies = image(icon = H.species.get_blood_mask(H), icon_state = blood_sprite_state)
|
||||
bloodsies.color = blood_color
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
//SUIT: Blood state is slightly different
|
||||
/obj/item/clothing/suit/apply_blood(var/image/standing)
|
||||
if(blood_DNA && blood_sprite_state && ishuman(loc))
|
||||
if(forensic_data?.has_blooddna() && blood_sprite_state && ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
blood_sprite_state = "[blood_overlay_type]blood"
|
||||
var/image/bloodsies = image(icon = H.species.get_blood_mask(H), icon_state = blood_sprite_state)
|
||||
|
||||
@@ -49,10 +49,9 @@
|
||||
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
|
||||
user.drop_item(src)
|
||||
var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc)
|
||||
N.fingerprints = src.fingerprints
|
||||
N.fingerprintshidden = src.fingerprintshidden
|
||||
N.fingerprintslast = src.fingerprintslast
|
||||
N.suit_fibers = src.suit_fibers
|
||||
transfer_blooddna_to(N)
|
||||
transfer_fingerprints_to(N)
|
||||
transfer_fibres_to(N)
|
||||
N.hailer = I
|
||||
I.loc = N
|
||||
if(!isturf(N.loc))
|
||||
|
||||
@@ -132,10 +132,9 @@
|
||||
else
|
||||
var/obj/N = new /obj/item/clothing/mask/gas/half(src.loc)
|
||||
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
|
||||
N.fingerprints = src.fingerprints
|
||||
N.fingerprintshidden = src.fingerprintshidden
|
||||
N.fingerprintslast = src.fingerprintslast
|
||||
N.suit_fibers = src.suit_fibers
|
||||
transfer_blooddna_to(N)
|
||||
transfer_fingerprints_to(N)
|
||||
transfer_fibres_to(N)
|
||||
if(!isturf(N.loc))
|
||||
user.put_in_hands(hailer)
|
||||
user.put_in_hands(N)
|
||||
|
||||
@@ -8,37 +8,117 @@ var/const/FINGERPRINT_COMPLETE = 6
|
||||
/proc/is_complete_print(var/print)
|
||||
return stringpercent(print) <= FINGERPRINT_COMPLETE
|
||||
|
||||
/atom/var/list/suit_fibers
|
||||
/// Forensics: Returns the object's forensic information datum. If none exists, it makes it.
|
||||
/atom/proc/init_forensic_data()
|
||||
RETURN_TYPE(/datum/forensics_crime)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
if(!forensic_data)
|
||||
forensic_data = new()
|
||||
return forensic_data
|
||||
|
||||
/atom/proc/add_fibers(mob/living/carbon/human/M)
|
||||
if(M.gloves && istype(M.gloves,/obj/item/clothing/gloves))
|
||||
var/obj/item/clothing/gloves/G = M.gloves
|
||||
if(G.transfer_blood) //bloodied gloves transfer blood to touched objects
|
||||
if(add_blood(G.bloody_hands_mob)) //only reduces the bloodiness of our gloves if the item wasn't already bloody
|
||||
/// Forensics: Handles most forensic investigation actions while touching an object. Including fingerprints, stray fibers from clothing, and bloody hands smearing objects. Returns true if a fingerprint was made.
|
||||
/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = FALSE)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
if(isnull(M) || isAI(M) || isnull(M.key))
|
||||
return FALSE
|
||||
|
||||
//Fibers from worn clothing get transfered along with fingerprints~
|
||||
var/datum/forensics_crime/C = init_forensic_data()
|
||||
C.add_fibres(M)
|
||||
|
||||
// bloodied gloves and hands transfer blood to touched objects. Blood does not transfer if we are already bloody.
|
||||
if(!forensic_data?.has_blooddna())
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(ishuman(M) && H.gloves && istype(H.gloves,/obj/item/clothing/gloves))
|
||||
var/obj/item/clothing/gloves/G = H.gloves
|
||||
if(G.transfer_blood)
|
||||
forensic_data.merge_blooddna(G.forensic_data)
|
||||
G.transfer_blood--
|
||||
else if(M.bloody_hands)
|
||||
if(add_blood(M.bloody_hands_mob))
|
||||
else if(M.bloody_hands)
|
||||
forensic_data.merge_blooddna(M.forensic_data)
|
||||
M.bloody_hands--
|
||||
|
||||
if(!suit_fibers) suit_fibers = list()
|
||||
var/fibertext
|
||||
var/item_multiplier = istype(src,/obj/item)?1.2:1
|
||||
var/suit_coverage = 0
|
||||
if(M.wear_suit)
|
||||
fibertext = "Material from \a [M.wear_suit]."
|
||||
if(prob(10*item_multiplier) && !(fibertext in suit_fibers))
|
||||
suit_fibers += fibertext
|
||||
suit_coverage = M.wear_suit.body_parts_covered
|
||||
//He has no prints!
|
||||
if(mFingerprints in M.mutations)
|
||||
if(C.get_lastprint() != M.key)
|
||||
C.add_hiddenprints(M)
|
||||
C.set_lastprint(M.key)
|
||||
return FALSE
|
||||
|
||||
if(M.w_uniform && (M.w_uniform.body_parts_covered & ~suit_coverage))
|
||||
fibertext = "Fibers from \a [M.w_uniform]."
|
||||
if(prob(15*item_multiplier) && !(fibertext in suit_fibers))
|
||||
suit_fibers += fibertext
|
||||
//Smudge up dem prints some if it's just a mob
|
||||
if(!ishuman(M))
|
||||
if(C.get_lastprint() != M.key)
|
||||
C.add_hiddenprints(M)
|
||||
C.set_lastprint(M.key)
|
||||
return TRUE
|
||||
|
||||
var/mob/living/carbon/human/H = M
|
||||
|
||||
//Now, deal with gloves.
|
||||
if (H.gloves && H.gloves != src)
|
||||
C.add_hiddenprints(M)
|
||||
H.gloves.add_fingerprint(M,ignoregloves)
|
||||
|
||||
//Deal with gloves the pass finger/palm prints.
|
||||
if(!ignoregloves)
|
||||
if(H.gloves && H.gloves != src)
|
||||
if(istype(H.gloves, /obj/item/clothing/gloves))
|
||||
var/obj/item/clothing/gloves/G = H.gloves
|
||||
if(!prob(G.fingerprint_chance))
|
||||
return 0
|
||||
|
||||
return C.add_prints(H)
|
||||
|
||||
/// Forensics: Adds an admin investigation fingerprint, even if no actual fingerprints are made. Used even if the action is done with a weapon as a way of logging actions for admins.
|
||||
/atom/proc/add_hiddenprint(mob/living/M as mob)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
if(isnull(M))
|
||||
return
|
||||
if(isnull(M.key))
|
||||
return
|
||||
init_forensic_data().add_hiddenprints(M)
|
||||
|
||||
/// Forensics: Adds blood dna to an object, this also usually gives the object a bloody overlay, but that is handled by the object itself. Returns true if this is the first time this dna is being added to this object.
|
||||
/atom/proc/add_blooddna(var/datum/dna/dna_data,var/mob/M)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
return init_forensic_data().add_blooddna(dna_data,M)
|
||||
|
||||
/// Forensics: Adds blood dna to an object, this version uses an organ's more restricted dna datum, but it still has all the information needed. Returns true if this is the first time this dna is being added to this object.
|
||||
/atom/proc/add_blooddna_organ(var/datum/organ_data/dna_data)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
return init_forensic_data().add_blooddna_organ(dna_data)
|
||||
|
||||
/// Forensics: Adds fibres from suits or gloves
|
||||
/atom/proc/add_fibres(mob/living/carbon/human/M)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
init_forensic_data().add_fibres(M)
|
||||
|
||||
/// Forensics: Transfers both our normal and hidden fingerprints to the specified object, handles the forensics datum creation itself.
|
||||
/atom/proc/transfer_fingerprints_to(var/atom/transfer_to)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
if(!forensic_data)
|
||||
return
|
||||
var/datum/forensics_crime/C = transfer_to.init_forensic_data()
|
||||
C.merge_prints(forensic_data)
|
||||
C.merge_hiddenprints(forensic_data)
|
||||
|
||||
/// Forensics: Transfers our blood dna to the specified object, handles the forensics datum creation itself.
|
||||
/atom/proc/transfer_blooddna_to(var/atom/transfer_to)
|
||||
if(!forensic_data)
|
||||
return
|
||||
transfer_to.init_forensic_data().merge_blooddna(forensic_data)
|
||||
|
||||
/// Forensics: Transfers our stray fibers to the specified object, handles the forensics datum creation itself.
|
||||
/atom/proc/transfer_fibres_to(var/atom/transfer_to)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
if(!forensic_data)
|
||||
return
|
||||
transfer_to.init_forensic_data().merge_fibres(forensic_data)
|
||||
|
||||
/// Forensics: Adds gunshot residue from firing boolets
|
||||
/atom/proc/add_gunshotresidue(var/obj/item/ammo_casing/shell)
|
||||
init_forensic_data().add_gunshotresidue(shell.caliber)
|
||||
|
||||
if(M.gloves && (M.gloves.body_parts_covered & ~suit_coverage))
|
||||
fibertext = "Material from a pair of [M.gloves.name]."
|
||||
if(prob(20*item_multiplier) && !(fibertext in suit_fibers))
|
||||
suit_fibers += "Material from a pair of [M.gloves.name]."
|
||||
|
||||
/datum/data/record/forensic
|
||||
name = "forensic data"
|
||||
@@ -48,9 +128,9 @@ var/const/FINGERPRINT_COMPLETE = 6
|
||||
uid = "\ref [A]"
|
||||
fields["name"] = sanitize(A.name)
|
||||
fields["area"] = sanitize("[get_area(A)]")
|
||||
fields["fprints"] = A.fingerprints ? A.fingerprints.Copy() : list()
|
||||
fields["fibers"] = A.suit_fibers ? A.suit_fibers.Copy() : list()
|
||||
fields["blood"] = A.blood_DNA ? A.blood_DNA.Copy() : list()
|
||||
fields["fprints"] = A.forensic_data?.get_prints().Copy()
|
||||
fields["fibers"] = A.forensic_data?.get_fibres().Copy()
|
||||
fields["blood"] = A.forensic_data?.get_blooddna().Copy()
|
||||
fields["time"] = world.time
|
||||
|
||||
/datum/data/record/forensic/proc/merge(var/datum/data/record/other)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/mob
|
||||
var/bloody_hands = 0
|
||||
var/mob/living/carbon/human/bloody_hands_mob
|
||||
var/track_blood = 0
|
||||
var/list/feet_blood_DNA
|
||||
var/track_blood_type
|
||||
@@ -8,7 +7,6 @@
|
||||
|
||||
/obj/item/clothing/gloves
|
||||
var/transfer_blood = 0
|
||||
var/mob/living/carbon/human/bloody_hands_mob
|
||||
|
||||
/obj/item/clothing/shoes/
|
||||
var/track_blood = 0
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
/obj/item/sample/Initialize(mapload, var/atom/supplied)
|
||||
. = ..()
|
||||
if(supplied)
|
||||
if(supplied && supplied.forensic_data)
|
||||
copy_evidence(supplied)
|
||||
name = "[initial(name)] (\the [supplied])"
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
icon_state = "fingerprint1"
|
||||
|
||||
/obj/item/sample/proc/copy_evidence(var/atom/supplied)
|
||||
if(supplied.suit_fibers && supplied.suit_fibers.len)
|
||||
evidence = supplied.suit_fibers.Copy()
|
||||
supplied.suit_fibers.Cut()
|
||||
var/list/fibre_data = supplied.forensic_data.get_fibres()
|
||||
if(fibre_data && fibre_data.len)
|
||||
evidence = fibre_data.Copy()
|
||||
supplied.forensic_data.clear_fibres()
|
||||
|
||||
/obj/item/sample/proc/merge_evidence(var/obj/item/sample/supplied, var/mob/user)
|
||||
if(!supplied.evidence || !supplied.evidence.len)
|
||||
@@ -116,10 +117,11 @@
|
||||
return 0
|
||||
|
||||
/obj/item/sample/print/copy_evidence(var/atom/supplied)
|
||||
if(supplied.fingerprints && supplied.fingerprints.len)
|
||||
for(var/print in supplied.fingerprints)
|
||||
evidence[print] = supplied.fingerprints[print]
|
||||
supplied.fingerprints.Cut()
|
||||
var/list/print_data = supplied.forensic_data.get_prints()
|
||||
if(print_data && print_data.len)
|
||||
for(var/print in print_data)
|
||||
evidence[print] = print_data[print]
|
||||
supplied.forensic_data.clear_prints()
|
||||
|
||||
/obj/item/forensics/sample_kit
|
||||
name = "fiber collection kit"
|
||||
@@ -130,7 +132,7 @@
|
||||
var/evidence_path = /obj/item/sample/fibers
|
||||
|
||||
/obj/item/forensics/sample_kit/proc/can_take_sample(var/mob/user, var/atom/supplied)
|
||||
return (supplied.suit_fibers && supplied.suit_fibers.len)
|
||||
return supplied.forensic_data?.has_fibres()
|
||||
|
||||
/obj/item/forensics/sample_kit/proc/take_sample(var/mob/user, var/atom/supplied)
|
||||
var/obj/item/sample/S = new evidence_path(get_turf(user), supplied)
|
||||
@@ -155,4 +157,4 @@
|
||||
evidence_path = /obj/item/sample/print
|
||||
|
||||
/obj/item/forensics/sample_kit/powder/can_take_sample(var/mob/user, var/atom/supplied)
|
||||
return (supplied.fingerprints && supplied.fingerprints.len)
|
||||
return supplied.forensic_data?.has_prints()
|
||||
|
||||
@@ -34,11 +34,12 @@
|
||||
to_chat(user, span_notice("Done printing."))
|
||||
// to_chat(user, span_notice("[M]'s Fingerprints: [md5(M.dna.uni_identity)]"))
|
||||
|
||||
if(reveal_blood && M.blood_DNA && M.blood_DNA.len)
|
||||
if(reveal_blood && M.forensic_data?.has_blooddna())
|
||||
to_chat(user, span_notice("Blood found on [M]. Analysing..."))
|
||||
spawn(15)
|
||||
for(var/blood in M.blood_DNA)
|
||||
to_chat(user, span_notice("Blood type: [M.blood_DNA[blood]]\nDNA: [blood]"))
|
||||
var/list/blooddna = M.forensic_data.get_blooddna()
|
||||
for(var/blood in blooddna)
|
||||
to_chat(user, span_notice("Blood type: [blooddna[blood]]\nDNA: [blood]"))
|
||||
return
|
||||
|
||||
/obj/item/detective_scanner/afterattack(atom/A as obj|turf, mob/user, proximity)
|
||||
@@ -67,7 +68,7 @@
|
||||
return 0
|
||||
|
||||
//General
|
||||
if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA)
|
||||
if (!A.forensic_data?.has_prints() && !A.forensic_data?.has_fibres() && !A.forensic_data.has_blooddna())
|
||||
user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\
|
||||
span_warning("Unable to locate any fingerprints, materials, fibers, or blood on [A]!"),\
|
||||
"You hear a faint hum of electrical equipment.")
|
||||
@@ -80,14 +81,15 @@
|
||||
return
|
||||
|
||||
//PRINTS
|
||||
if(A.fingerprints && A.fingerprints.len)
|
||||
to_chat(user, span_notice("Isolated [A.fingerprints.len] fingerprints:"))
|
||||
if(A.forensic_data?.has_prints())
|
||||
to_chat(user, span_notice("Isolated [A.forensic_data?.get_prints().len] fingerprints:"))
|
||||
if(!reveal_incompletes)
|
||||
to_chat(user, span_warning("Rapid Analysis Imperfect: Scan samples with H.R.F.S. equipment to determine nature of incomplete prints."))
|
||||
var/list/complete_prints = list()
|
||||
var/list/incomplete_prints = list()
|
||||
for(var/i in A.fingerprints)
|
||||
var/print = A.fingerprints[i]
|
||||
var/list/print_data = A.forensic_data?.get_prints()
|
||||
for(var/i in print_data)
|
||||
var/print = print_data[i]
|
||||
if(stringpercent(print) <= FINGERPRINT_COMPLETE)
|
||||
complete_prints += print
|
||||
else
|
||||
@@ -107,21 +109,22 @@
|
||||
|
||||
|
||||
//FIBERS
|
||||
if(A.suit_fibers && A.suit_fibers.len)
|
||||
if(A.forensic_data?.has_fibres())
|
||||
to_chat(user,span_notice("Fibers/Materials detected.[reveal_fibers ? " Analysing..." : " Acquisition of fibers for H.R.F.S. analysis advised."]"))
|
||||
flick("[icon_state]1",src)
|
||||
if(reveal_fibers && do_after(user, 5 SECONDS))
|
||||
to_chat(user, span_notice("Apparel samples scanned:"))
|
||||
for(var/sample in A.suit_fibers)
|
||||
for(var/sample in A.forensic_data.get_fibres())
|
||||
to_chat(user, " - " + span_notice("[sample]"))
|
||||
|
||||
//Blood
|
||||
if (A.blood_DNA && A.blood_DNA.len)
|
||||
if (A.forensic_data?.has_blooddna())
|
||||
to_chat(user, span_notice("Blood detected.[reveal_blood ? " Analysing..." : " Acquisition of swab for H.R.F.S. analysis advised."]"))
|
||||
if(reveal_blood && do_after(user, 5 SECONDS))
|
||||
flick("[icon_state]1",src)
|
||||
for(var/blood in A.blood_DNA)
|
||||
to_chat(user, "Blood type: " + span_warning("[A.blood_DNA[blood]]") + " DNA: " + span_warning("[blood]"))
|
||||
var/list/blood_data = A.forensic_data.get_blooddna()
|
||||
for(var/blood in blood_data)
|
||||
to_chat(user, "Blood type: " + span_warning("[blood_data[blood]]") + " DNA: " + span_warning("[blood]"))
|
||||
|
||||
user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\
|
||||
span_notice("You finish scanning \the [A]."),\
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
return
|
||||
user.visible_message("[user] swabs [H]'s palm for a sample.")
|
||||
sample_type = "GSR"
|
||||
gsr = H.gunshot_residue
|
||||
gsr = H.forensic_data?.get_gunshotresidue()
|
||||
else
|
||||
return
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
add_fingerprint(user)
|
||||
|
||||
var/list/choices = list()
|
||||
if(A.blood_DNA)
|
||||
if(A.forensic_data?.has_blooddna())
|
||||
choices |= "Blood"
|
||||
if(istype(A, /obj/item/clothing))
|
||||
choices |= "Gunshot Residue"
|
||||
@@ -99,16 +99,16 @@
|
||||
|
||||
var/sample_type
|
||||
if(choice == "Blood")
|
||||
if(!A.blood_DNA || !A.blood_DNA.len) return
|
||||
dna = A.blood_DNA.Copy()
|
||||
if(!A.forensic_data?.has_blooddna()) return
|
||||
dna = A.forensic_data?.get_blooddna().Copy()
|
||||
sample_type = "blood"
|
||||
|
||||
else if(choice == "Gunshot Residue")
|
||||
var/obj/item/clothing/B = A
|
||||
if(!istype(B) || !B.gunshot_residue)
|
||||
if(!istype(B) || !B.forensic_data?.get_gunshotresidue())
|
||||
to_chat(user, span_warning("There is no residue on \the [A]."))
|
||||
return
|
||||
gsr = B.gunshot_residue
|
||||
gsr = B.forensic_data?.get_gunshotresidue()
|
||||
sample_type = "residue"
|
||||
|
||||
if(sample_type)
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
attached_grenade.det_time = between(1, detonation_time.data, 12) SECONDS
|
||||
attached_grenade.activate()
|
||||
var/atom/holder = loc
|
||||
log_and_message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]")
|
||||
log_and_message_admins("activated a grenade assembly. Last touches: Assembly: [holder.forensic_data?.get_lastprint()] Circuit: [forensic_data?.get_lastprint()] Grenade: [attached_grenade.forensic_data?.get_lastprint()]")
|
||||
|
||||
// These procs do not relocate the grenade, that's the callers responsibility
|
||||
/obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/grenade/G)
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
clothing_slots += list(H.l_store, H.r_store)
|
||||
|
||||
for(var/obj/item/clothing/C in clothing_slots)
|
||||
if(C.blood_DNA && C.blood_color && C.blood_color != SYNTH_BLOOD_COLOUR)
|
||||
if(C.forensic_data?.has_blooddna() && C.blood_color && C.blood_color != SYNTH_BLOOD_COLOUR)
|
||||
human_blood_fear_amount += 1
|
||||
|
||||
// This is divided, since humans can wear so many items at once.
|
||||
@@ -150,7 +150,7 @@
|
||||
// Bloody objects are also bad.
|
||||
if(istype(thing, /obj))
|
||||
var/obj/O = thing
|
||||
if(O.blood_DNA && O.blood_color && O.blood_color != SYNTH_BLOOD_COLOUR)
|
||||
if(O.forensic_data?.has_blooddna() && O.blood_color && O.blood_color != SYNTH_BLOOD_COLOUR)
|
||||
fear_amount++
|
||||
|
||||
return fear_amount
|
||||
|
||||
@@ -123,14 +123,14 @@
|
||||
accessory_descs += "<a href='byond://?src=\ref[src];lookitem_desc_only=\ref[A]'>\a [A]</a>"
|
||||
|
||||
tie_msg += " [lowertext(english_list(accessory_descs))]."
|
||||
if(w_uniform.blood_DNA)
|
||||
if(w_uniform.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] wearing [icon2html(w_uniform,user.client)] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[w_uniform]'>[w_uniform.name]</a>![tie_msg]")
|
||||
else
|
||||
msg += "[T.He] [T.is] wearing [icon2html(w_uniform,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[w_uniform]'>\a [w_uniform]</a>.[tie_msg]"
|
||||
|
||||
//head
|
||||
if(head && !(skip_gear & EXAMINE_SKIPHELMET) && head.show_examine)
|
||||
if(head.blood_DNA)
|
||||
if(head.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] wearing [icon2html(head,user.client)] [head.gender==PLURAL?"some":"a"] [(head.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[head]'>[head.name]</a> on [T.his] head!")
|
||||
else
|
||||
msg += "[T.He] [T.is] wearing [icon2html(head,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[head]'>\a [head]</a> on [T.his] head."
|
||||
@@ -147,35 +147,35 @@
|
||||
accessory_descs += "<a href='byond://?src=\ref[src];lookitem_desc_only=\ref[accessory]'>\a [accessory]</a>"
|
||||
tie_msg += " [lowertext(english_list(accessory_descs))]."
|
||||
|
||||
if(wear_suit.blood_DNA)
|
||||
if(wear_suit.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] wearing [icon2html(wear_suit,user.client)] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[wear_suit]'>[wear_suit.name]</a>![tie_msg]")
|
||||
else
|
||||
msg += "[T.He] [T.is] wearing [icon2html(wear_suit,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[wear_suit]'>\a [wear_suit]</a>.[tie_msg]"
|
||||
|
||||
//suit/armour storage
|
||||
if(s_store && !(skip_gear & EXAMINE_SKIPSUITSTORAGE) && s_store.show_examine)
|
||||
if(s_store.blood_DNA)
|
||||
if(s_store.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] carrying [icon2html(s_store,user.client)] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[s_store]'>[s_store.name]</a> on [T.his] [wear_suit.name]!")
|
||||
else
|
||||
msg += "[T.He] [T.is] carrying [icon2html(s_store,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[s_store]'>\a [s_store]</a> on [T.his] [wear_suit.name]."
|
||||
|
||||
//back
|
||||
if(back && !(skip_gear & EXAMINE_SKIPBACKPACK) && back.show_examine)
|
||||
if(back.blood_DNA)
|
||||
if(back.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.has] [icon2html(back,user.client)] [back.gender==PLURAL?"some":"a"] [(back.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[back]'>[back]</a> on [T.his] back.")
|
||||
else
|
||||
msg += "[T.He] [T.has] [icon2html(back,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[back]'>\a [back]</a> on [T.his] back."
|
||||
|
||||
//left hand
|
||||
if(l_hand && l_hand.show_examine)
|
||||
if(l_hand.blood_DNA)
|
||||
if(l_hand.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] holding [icon2html(l_hand,user.client)] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[l_hand]'>[l_hand.name]</a> in [T.his] left hand!")
|
||||
else
|
||||
msg += "[T.He] [T.is] holding [icon2html(l_hand,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[l_hand]'>\a [l_hand]</a> in [T.his] left hand."
|
||||
|
||||
//right hand
|
||||
if(r_hand && r_hand.show_examine)
|
||||
if(r_hand.blood_DNA)
|
||||
if(r_hand.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] holding [icon2html(r_hand,user.client)] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[r_hand]'>[r_hand.name]</a> in [T.his] right hand!")
|
||||
else
|
||||
msg += "[T.He] [T.is] holding [icon2html(r_hand,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[r_hand]'>\a [r_hand]</a> in [T.his] right hand."
|
||||
@@ -192,12 +192,12 @@
|
||||
accessory_descs += "<a href='byond://?src=\ref[src];lookitem_desc_only=\ref[A]'>\a [A]</a>"
|
||||
|
||||
gloves_acc_msg += " [lowertext(english_list(accessory_descs))]."
|
||||
if(gloves.blood_DNA)
|
||||
if(gloves.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.has] [icon2html(gloves,user.client)] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[gloves]'>[gloves.name]</a> on [T.his] hands![gloves_acc_msg]")
|
||||
else
|
||||
msg += "[T.He] [T.has] [icon2html(gloves,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[gloves]'>\a [gloves]</a> on [T.his] hands.[gloves_acc_msg]"
|
||||
|
||||
else if(blood_DNA && !(skip_body & EXAMINE_SKIPHANDS))
|
||||
else if(forensic_data?.has_blooddna() && !(skip_body & EXAMINE_SKIPHANDS))
|
||||
msg += span_warning("[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!")
|
||||
|
||||
//handcuffed?
|
||||
@@ -213,14 +213,14 @@
|
||||
|
||||
//belt
|
||||
if(belt && !(skip_gear & EXAMINE_SKIPBELT) && belt.show_examine)
|
||||
if(belt.blood_DNA)
|
||||
if(belt.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.has] [icon2html(belt,user.client)] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[belt]'>[belt.name]</a> about [T.his] waist!")
|
||||
else
|
||||
msg += "[T.He] [T.has] [icon2html(belt,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[belt]'>\a [belt]</a> about [T.his] waist."
|
||||
|
||||
//shoes
|
||||
if(shoes && !(skip_gear & EXAMINE_SKIPSHOES) && shoes.show_examine)
|
||||
if(shoes.blood_DNA)
|
||||
if(shoes.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.is] wearing [icon2html(shoes,user.client)] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[shoes]'>[shoes.name]</a> on [T.his] feet!")
|
||||
else
|
||||
msg += "[T.He] [T.is] wearing [icon2html(shoes,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[shoes]'>\a [shoes]</a> on [T.his] feet."
|
||||
@@ -233,14 +233,14 @@
|
||||
if(istype(wear_mask, /obj/item/grenade) && check_has_mouth())
|
||||
descriptor = "in [T.his] mouth"
|
||||
|
||||
if(wear_mask.blood_DNA)
|
||||
if(wear_mask.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.has] [icon2html(wear_mask,user.client)] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[wear_mask]'>[wear_mask.name]</a> [descriptor]!")
|
||||
else
|
||||
msg += "[T.He] [T.has] [icon2html(wear_mask,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[wear_mask]'>\a [wear_mask]</a> [descriptor]."
|
||||
|
||||
//eyes
|
||||
if(glasses && !(skip_gear & EXAMINE_SKIPEYEWEAR) && glasses.show_examine)
|
||||
if(glasses.blood_DNA)
|
||||
if(glasses.forensic_data?.has_blooddna())
|
||||
msg += span_warning("[T.He] [T.has] [icon2html(glasses,user.client)] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != "#030303") ? "blood" : "oil"]-stained <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[glasses]'>[glasses]</a> covering [T.his] eyes!")
|
||||
else
|
||||
msg += "[T.He] [T.has] [icon2html(glasses,user.client)] <a href='byond://?src=\ref[src];lookitem_desc_only=\ref[glasses]'>\a [glasses]</a> covering [T.his] eyes."
|
||||
|
||||
@@ -1137,8 +1137,7 @@
|
||||
return 0
|
||||
//if this blood isn't already in the list, add it
|
||||
if(istype(M))
|
||||
if(!blood_DNA[M.dna.unique_enzymes])
|
||||
blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
|
||||
add_blooddna(M.dna,M)
|
||||
hand_blood_color = blood_color
|
||||
update_bloodied()
|
||||
add_verb(src, /mob/living/carbon/human/proc/bloody_doodle)
|
||||
@@ -1152,8 +1151,6 @@
|
||||
/mob/living/carbon/human/wash(clean_types)
|
||||
. = ..()
|
||||
|
||||
gunshot_residue = null
|
||||
|
||||
//Always do hands (or whatever's on our hands)
|
||||
if(gloves)
|
||||
gloves.wash(clean_types)
|
||||
|
||||
@@ -577,11 +577,9 @@ emp_act
|
||||
var/obj/item/clothing/gloves/gl = gloves
|
||||
gl.add_blood(source)
|
||||
gl.transfer_blood = amount
|
||||
gl.bloody_hands_mob = source
|
||||
else
|
||||
add_blood(source)
|
||||
bloody_hands = amount
|
||||
bloody_hands_mob = source
|
||||
update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves
|
||||
|
||||
/mob/living/carbon/human/proc/bloody_body(var/mob/living/source)
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
|
||||
var/age = 30 //Player's age (pure fluff)
|
||||
|
||||
var/b_type = "A+" //Player's bloodtype
|
||||
var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso). Also holds the datum for the type of robolimb.
|
||||
|
||||
var/list/all_underwear = list()
|
||||
@@ -92,7 +91,6 @@
|
||||
var/hand_blood_color
|
||||
|
||||
var/list/flavor_texts = list()
|
||||
var/gunshot_residue
|
||||
var/pulling_punches // Are you trying not to hurt your opponent?
|
||||
var/robolimb_count = 0 // Total number of external robot parts.
|
||||
var/robobody_count = 0 // Counts torso, groin, and head, if they're robotic
|
||||
|
||||
@@ -161,7 +161,7 @@ var/static/icon/ingame_hud_med_vr = icon('icons/mob/hud_med_vr.dmi')
|
||||
grad_style = character.grad_style
|
||||
f_style = character.f_style
|
||||
grad_style = character.grad_style
|
||||
b_type = character.b_type
|
||||
dna?.b_type = character.dna ? character.dna.b_type : DEFAULT_BLOOD_TYPE
|
||||
synth_color = character.synth_color
|
||||
r_synth = character.r_synth
|
||||
g_synth = character.g_synth
|
||||
|
||||
@@ -464,8 +464,8 @@
|
||||
return FALSE
|
||||
|
||||
|
||||
log_admin("[key_name_admin(src)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.fingerprintslast].")
|
||||
message_admins("[key_name_admin(src)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.fingerprintslast]. (<A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[T.x];Y=[T.y];Z=[T.z]'>JMP</a>)", 1)
|
||||
log_admin("[key_name_admin(src)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.forensic_data?.get_lastprint()].")
|
||||
message_admins("[key_name_admin(src)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.forensic_data?.get_lastprint()]. (<A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[T.x];Y=[T.y];Z=[T.z]'>JMP</a>)", 1)
|
||||
// start the dephase
|
||||
phase_in(T)
|
||||
shadekin_adjust_energy(-20) // loss of energy for the interception
|
||||
|
||||
@@ -238,8 +238,7 @@ var/datum/species/shapeshifter/promethean/prometheans
|
||||
H.feet_blood_color = null
|
||||
H.adjust_nutrition(rand(3, 10))
|
||||
if(H.bloody_hands)
|
||||
LAZYCLEARLIST(H.blood_DNA)
|
||||
H.blood_DNA = null
|
||||
H.forensic_data?.clear_blooddna()
|
||||
H.hand_blood_color = null
|
||||
H.bloody_hands = 0
|
||||
H.adjust_nutrition(rand(3, 10))
|
||||
|
||||
@@ -394,13 +394,14 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) //see UpdateDamageIcon()
|
||||
return
|
||||
|
||||
remove_layer(BLOOD_LAYER)
|
||||
if(!blood_DNA && !feet_blood_DNA)
|
||||
var/bloody_mess = forensic_data?.get_blooddna()
|
||||
if(!bloody_mess && !feet_blood_DNA)
|
||||
return
|
||||
|
||||
var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+BLOOD_LAYER)
|
||||
|
||||
//Bloody hands
|
||||
if(blood_DNA)
|
||||
if(bloody_mess)
|
||||
var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "bloodyhands", layer = BODY_LAYER+BLOOD_LAYER)
|
||||
bloodsies.color = hand_blood_color
|
||||
both.add_overlay(bloodsies)
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
to_chat(O, span_warning("[src] launches a razor-sharp quill at [target]!"))
|
||||
|
||||
var/obj/item/arrow/quill/Q = new(loc)
|
||||
Q.fingerprintslast = src.ckey
|
||||
Q.add_fingerprint(ckey)
|
||||
Q.throw_at(target,10,30)
|
||||
quills--
|
||||
|
||||
|
||||
@@ -468,11 +468,12 @@ var/const/CE_STABLE_THRESHOLD = 0.5
|
||||
|
||||
// Update blood information.
|
||||
if(source.data["blood_DNA"])
|
||||
B.blood_DNA = list()
|
||||
var/list/new_data = list()
|
||||
if(source.data["blood_type"])
|
||||
B.blood_DNA[source.data["blood_DNA"]] = source.data["blood_type"]
|
||||
new_data[source.data["blood_DNA"]] = source.data["blood_type"]
|
||||
else
|
||||
B.blood_DNA[source.data["blood_DNA"]] = "O+"
|
||||
new_data[source.data["blood_DNA"]] = "O+"
|
||||
B.init_forensic_data().merge_blooddna(null,new_data)
|
||||
|
||||
// Update virus information.
|
||||
if(source.data["viruses"])
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
self_clear = TRUE
|
||||
|
||||
// Setup cached dna data, as storing the entire DNA cloned is horrifically laggy
|
||||
b_type = dna.b_type
|
||||
unique_enzymes = dna.unique_enzymes
|
||||
body_gender = dna.GetUIState(DNA_UI_GENDER)
|
||||
if(!isnull(dna.GetUIValue(DNA_UI_SKIN_TONE)))
|
||||
|
||||
@@ -105,9 +105,7 @@ var/list/organ_cache = list()
|
||||
E.internal_organs = list()
|
||||
E.internal_organs |= src
|
||||
if(data)
|
||||
if(!blood_DNA)
|
||||
blood_DNA = list()
|
||||
blood_DNA[data.unique_enzymes] = data.b_type
|
||||
add_blooddna_organ(data)
|
||||
else
|
||||
data.setup_from_species(GLOB.all_species["Human"])
|
||||
|
||||
@@ -131,9 +129,8 @@ var/list/organ_cache = list()
|
||||
/obj/item/organ/proc/set_dna(var/datum/dna/new_dna)
|
||||
if(new_dna)
|
||||
data.setup_from_dna(new_dna)
|
||||
if(blood_DNA)
|
||||
blood_DNA.Cut()
|
||||
blood_DNA[data.unique_enzymes] = data.b_type
|
||||
forensic_data?.clear_blooddna()
|
||||
add_blooddna_organ(data)
|
||||
|
||||
/obj/item/organ/proc/die()
|
||||
if(robotic < ORGAN_ROBOT)
|
||||
@@ -460,10 +457,8 @@ var/list/organ_cache = list()
|
||||
|
||||
// Pass over the blood.
|
||||
reagents.trans_to(O, reagents.total_volume)
|
||||
|
||||
if(fingerprints) O.fingerprints = fingerprints.Copy()
|
||||
if(fingerprintshidden) O.fingerprintshidden = fingerprintshidden.Copy()
|
||||
if(fingerprintslast) O.fingerprintslast = fingerprintslast
|
||||
transfer_fingerprints_to(O)
|
||||
transfer_blooddna_to(O)
|
||||
|
||||
user.put_in_active_hand(O)
|
||||
qdel(src)
|
||||
|
||||
@@ -293,7 +293,7 @@ var/list/civilian_cartridges = list(
|
||||
status_signal.data["msg2"] = data2
|
||||
if(loc)
|
||||
var/obj/item/PDA = loc
|
||||
var/mob/user = PDA.fingerprintslast
|
||||
var/mob/user = PDA.forensic_data?.get_lastprint()
|
||||
log_admin("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]")
|
||||
message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]")
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
if("message")
|
||||
status_signal.data["msg1"] = data1
|
||||
status_signal.data["msg2"] = data2
|
||||
var/mob/user = pda.fingerprintslast
|
||||
var/mob/user = pda.forensic_data?.get_lastprint()
|
||||
if(isliving(pda.loc))
|
||||
user = pda.loc
|
||||
log_admin("STATUS: [user] set status screen with [pda]. Message: [data1] [data2]")
|
||||
|
||||
@@ -85,15 +85,14 @@
|
||||
scan_blood(A, user)
|
||||
|
||||
/datum/data/pda/utility/scanmode/dna/proc/scan_blood(atom/A, mob/user)
|
||||
if(!A.blood_DNA)
|
||||
var/list/blood_dna = A.forensic_data?.get_blooddna()
|
||||
if(!blood_dna)
|
||||
to_chat(user, span_notice("No blood found on [A]"))
|
||||
if(A.blood_DNA)
|
||||
qdel(A.blood_DNA)
|
||||
else
|
||||
to_chat(user, span_notice("Blood found on [A]. Analysing..."))
|
||||
spawn(15)
|
||||
for(var/blood in A.blood_DNA)
|
||||
to_chat(user, span_notice("Blood type: [A.blood_DNA[blood]]\nDNA: [blood]"))
|
||||
for(var/blood in blood_dna)
|
||||
to_chat(user, span_notice("Blood type: [blood_dna[blood]]\nDNA: [blood]"))
|
||||
|
||||
/datum/data/pda/utility/scanmode/halogen
|
||||
base_name = "Halogen Counter"
|
||||
|
||||
@@ -189,8 +189,8 @@
|
||||
return
|
||||
//explosion(T, 0, 1, 2, 2)
|
||||
|
||||
log_admin("LOG: Rigged power cell explosion, last touched by [fingerprintslast]")
|
||||
message_admins("LOG: Rigged power cell explosion, last touched by [fingerprintslast]")
|
||||
log_admin("LOG: Rigged power cell explosion, last touched by [forensic_data?.get_lastprint()]")
|
||||
message_admins("LOG: Rigged power cell explosion, last touched by [forensic_data?.get_lastprint()]")
|
||||
|
||||
explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range)
|
||||
|
||||
|
||||
@@ -485,8 +485,8 @@ var/global/list/light_type_cache = list()
|
||||
if(rigged)
|
||||
if(status == LIGHT_OK && trigger)
|
||||
|
||||
log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]")
|
||||
message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]")
|
||||
log_admin("LOG: Rigged light explosion, last touched by [forensic_data?.get_lastprint()]")
|
||||
message_admins("LOG: Rigged light explosion, last touched by [forensic_data?.get_lastprint()]")
|
||||
|
||||
explode()
|
||||
else if( prob( min(60, switchcount*switchcount*0.01) ) )
|
||||
@@ -600,8 +600,8 @@ var/global/list/light_type_cache = list()
|
||||
|
||||
if(on && rigged)
|
||||
|
||||
log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]")
|
||||
message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]")
|
||||
log_admin("LOG: Rigged light explosion, last touched by [forensic_data?.get_lastprint()]")
|
||||
message_admins("LOG: Rigged light explosion, last touched by [forensic_data?.get_lastprint()]")
|
||||
|
||||
explode()
|
||||
|
||||
|
||||
@@ -74,12 +74,12 @@
|
||||
return
|
||||
|
||||
var/prints = ""
|
||||
if(src.fingerprintshidden)
|
||||
prints = ", all touchers : " + src.fingerprintshidden
|
||||
if(forensic_data?.get_hiddenprints())
|
||||
prints = ", all touchers : " + forensic_data?.get_hiddenprints()
|
||||
|
||||
SetUniversalState(/datum/universal_state/supermatter_cascade)
|
||||
log_admin("New super singularity made by eating a SM crystal [prints]. Last touched by [src.fingerprintslast].")
|
||||
message_admins("New super singularity made by eating a SM crystal [prints]. Last touched by [src.fingerprintslast].")
|
||||
log_admin("New super singularity made by eating a SM crystal [prints]. Last touched by [forensic_data?.get_lastprint()].")
|
||||
message_admins("New super singularity made by eating a SM crystal [prints]. Last touched by [forensic_data?.get_lastprint()].")
|
||||
qdel(src)
|
||||
return 50000
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
var/obj/item/stack/rods/R = W
|
||||
if (R.use(1))
|
||||
bolt = new /obj/item/arrow/rod(src)
|
||||
bolt.fingerprintslast = src.fingerprintslast
|
||||
bolt.add_fingerprint(user)
|
||||
bolt.loc = src
|
||||
update_icon()
|
||||
user.visible_message("[user] jams [bolt] into [src].","You jam [bolt] into [src].")
|
||||
|
||||
@@ -87,10 +87,10 @@
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(istype(H))
|
||||
if(!istype(H.gloves, /obj/item/clothing))
|
||||
H.gunshot_residue = chambered.caliber
|
||||
H.add_gunshotresidue(chambered)
|
||||
else
|
||||
var/obj/item/clothing/G = H.gloves
|
||||
G.gunshot_residue = chambered.caliber
|
||||
G.add_gunshotresidue(chambered)
|
||||
|
||||
switch(handle_casings)
|
||||
if(EJECT_CASINGS) //eject casing onto ground.
|
||||
|
||||
@@ -203,10 +203,10 @@
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(istype(H))
|
||||
if(!istype(H.gloves, /obj/item/clothing))
|
||||
H.gunshot_residue = chambered.caliber
|
||||
H.add_gunshotresidue(chambered)
|
||||
else
|
||||
var/obj/item/clothing/G = H.gloves
|
||||
G.gunshot_residue = chambered.caliber
|
||||
G.add_gunshotresidue(chambered)
|
||||
|
||||
switch(handle_casings)
|
||||
if(EJECT_CASINGS) //eject casing onto ground.
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
if(!data["donor"] || ishuman(data["donor"]))
|
||||
blood_splatter(T, src, 1)
|
||||
else if(istype(data["donor"], /mob/living/carbon/alien))
|
||||
var/mob/living/carbon/alien/A = data["donor"]
|
||||
var/obj/effect/decal/cleanable/blood/B = blood_splatter(T, src, 1)
|
||||
if(B)
|
||||
B.blood_DNA["UNKNOWN DNA STRUCTURE"] = "X*"
|
||||
B.add_blooddna(A.dna,A)
|
||||
|
||||
/datum/reagent/blood/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
if(LAZYLEN(SS.breaches))
|
||||
to_chat(user, span_warning("You should probably repair that before you start tinkering with it."))
|
||||
return
|
||||
if(O.blood_DNA || O.contaminated) //check if we're bloody or gooey or whatever, so modkits can't be used to hide crimes easily.
|
||||
if(O.forensic_data?.has_blooddna() || O.contaminated) //check if we're bloody or gooey or whatever, so modkits can't be used to hide crimes easily.
|
||||
to_chat(user, span_warning("You should probably clean that up before you start tinkering with it."))
|
||||
return
|
||||
//we have to check that it's not the original type first, because otherwise it might convert wrong based on pathing; the subtype can still count as the basetype
|
||||
@@ -107,11 +107,9 @@
|
||||
var/obj/N = new to_type(O.loc)
|
||||
user.visible_message(span_notice("[user] opens \the [src] and modifies \the [O] into \the [N]."),span_notice("You open \the [src] and modify \the [O] into \the [N]."))
|
||||
|
||||
//crude, but transfer prints and fibers to avoid forensics abuse, same as the bloody/gooey check above
|
||||
N.fingerprints = O.fingerprints
|
||||
N.fingerprintshidden = O.fingerprintshidden
|
||||
N.fingerprintslast = O.fingerprintslast
|
||||
N.suit_fibers = O.suit_fibers
|
||||
// Transfer forensics to, lets avoid CRIME exploits
|
||||
O.transfer_fingerprints_to(N)
|
||||
O.transfer_fibres_to(N)
|
||||
|
||||
//transfer logic could technically be made more thorough and handle stuff like helmet/boots/tank vars for suits, but in those cases you should be removing the items first anyway
|
||||
if(skip_content_check && transfer_contents)
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
prefs.s_tone = character.s_tone
|
||||
prefs.h_style = character.h_style
|
||||
prefs.f_style = character.f_style
|
||||
prefs.b_type = character.b_type
|
||||
prefs.b_type = character.dna ? character.dna.b_type : DEFAULT_BLOOD_TYPE
|
||||
|
||||
// Saves mob's current custom species, ears, tail, wings and digitigrade legs state to prefs
|
||||
// This basically needs to be the reverse of /datum/category_item/player_setup_item/vore/ears/copy_to_mob() ~Leshana
|
||||
|
||||
@@ -32,8 +32,7 @@
|
||||
B.basecolor = M.species.get_blood_colour(M)
|
||||
B.color = M.species.get_blood_colour(M)
|
||||
B.target_turf = pick(RANGE_TURFS(1, holder))
|
||||
B.blood_DNA = list()
|
||||
B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
|
||||
B.add_blooddna(M.dna,M)
|
||||
var/blood_to_remove = (rand(10,30))
|
||||
M.remove_blood(blood_to_remove)
|
||||
if(harvested)
|
||||
|
||||
@@ -137,8 +137,7 @@
|
||||
to_chat(M, span_red("The skin on your [parse_zone(target)] feels like it's ripping apart, and a stream of blood flies out."))
|
||||
var/obj/effect/decal/cleanable/blood/splatter/animated/B = new(M.loc)
|
||||
B.target_turf = pick(range(1, src))
|
||||
B.blood_DNA = list()
|
||||
B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
|
||||
B.add_blooddna(M.dna,M)
|
||||
M.remove_blood(rand(25,50))
|
||||
|
||||
//animated blood 2 SPOOKY
|
||||
@@ -161,13 +160,13 @@
|
||||
//leave some drips behind
|
||||
if(prob(50))
|
||||
var/obj/effect/decal/cleanable/blood/drip/D = new(src.loc)
|
||||
D.blood_DNA = src.blood_DNA.Copy()
|
||||
D.add_blooddna(forensic_data)
|
||||
if(prob(50))
|
||||
D = new(src.loc)
|
||||
D.blood_DNA = src.blood_DNA.Copy()
|
||||
D.add_blooddna(forensic_data)
|
||||
if(prob(50))
|
||||
D = new(src.loc)
|
||||
D.blood_DNA = src.blood_DNA.Copy()
|
||||
D.add_blooddna(forensic_data)
|
||||
else
|
||||
..()
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
required = /obj/item/slime_extract/orange
|
||||
|
||||
/decl/chemical_reaction/instant/slime/orange_fire/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Orange extract reaction (fire) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Orange extract reaction (fire) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
holder.my_atom.visible_message(span_danger("\The [src] begins to vibrate violently!"))
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
spawn(5 SECONDS)
|
||||
@@ -370,7 +370,7 @@
|
||||
if(!Z) // Paranoid.
|
||||
return
|
||||
|
||||
log_and_message_admins("Orange extract reaction (heat wave) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Orange extract reaction (heat wave) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
|
||||
var/list/nearby_things = view(T)
|
||||
|
||||
@@ -453,7 +453,7 @@
|
||||
required = /obj/item/slime_extract/yellow
|
||||
|
||||
/decl/chemical_reaction/instant/slime/yellow_lightning/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Yellow extract reaction (lightning) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Yellow extract reaction (lightning) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
holder.my_atom.visible_message(span_danger("\The [src] begins to vibrate violently!"))
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
spawn(5 SECONDS)
|
||||
@@ -484,7 +484,7 @@
|
||||
required = /obj/item/slime_extract/yellow
|
||||
|
||||
/decl/chemical_reaction/instant/slime/yellow_emp/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Yellow extract reaction (emp) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Yellow extract reaction (emp) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
holder.my_atom.visible_message(span_danger("\The [src] begins to vibrate violently!"))
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
spawn(5 SECONDS)
|
||||
@@ -525,7 +525,7 @@
|
||||
required = /obj/item/slime_extract/gold
|
||||
|
||||
/decl/chemical_reaction/instant/slime/gold_random_mobs/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Gold extract reaction (random mobs) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Gold extract reaction (random mobs) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
var/type_to_spawn
|
||||
var/list/all_spawnable_types = list()
|
||||
all_spawnable_types += xenobio_gold_mobs_safe
|
||||
@@ -551,7 +551,7 @@
|
||||
required = /obj/item/slime_extract/gold
|
||||
|
||||
/decl/chemical_reaction/instant/slime/gold_hostile_mob/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Gold extract reaction (dangerous mob) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Gold extract reaction (dangerous mob) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
var/type_to_spawn = pickweight(xenobio_gold_mobs_hostile)
|
||||
var/mob/living/C = new type_to_spawn(get_turf(holder.my_atom))
|
||||
for(var/l = 1, l <= rand(1, 3), l++)
|
||||
@@ -724,7 +724,7 @@
|
||||
if(!Z) // Paranoid.
|
||||
return
|
||||
|
||||
log_and_message_admins("Dark Blue extract reaction (cold snap) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Dark Blue extract reaction (cold snap) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
|
||||
var/list/nearby_things = view(T)
|
||||
|
||||
@@ -874,7 +874,7 @@
|
||||
H.add_modifier(/datum/modifier/berserk, 30 SECONDS)
|
||||
to_chat(H, span_warning("An intense wave of rage is felt from inside, but you remain in control of yourself."))
|
||||
|
||||
log_and_message_admins("Red extract reaction (enrage) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Red extract reaction (enrage) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
..()
|
||||
@@ -920,7 +920,7 @@
|
||||
required = /obj/item/slime_extract/green
|
||||
|
||||
/decl/chemical_reaction/instant/slime/green_radpulse/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Green extract reaction (radiation pulse) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Green extract reaction (radiation pulse) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
holder.my_atom.visible_message(span_danger("\The [holder.my_atom] begins to vibrate violently!"))
|
||||
spawn(5 SECONDS)
|
||||
@@ -936,7 +936,7 @@
|
||||
required = /obj/item/slime_extract/green
|
||||
|
||||
/decl/chemical_reaction/instant/slime/green_emitter/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Green extract reaction (radiation emitter) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Green extract reaction (radiation emitter) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
new /obj/item/slime_irradiator(get_turf(holder.my_atom))
|
||||
..()
|
||||
|
||||
@@ -1079,7 +1079,7 @@
|
||||
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
holder.my_atom.visible_message(span_danger("\The [holder.my_atom] begins to vibrate violently!"))
|
||||
log_and_message_admins("Oil extract reaction (explosion) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Oil extract reaction (explosion) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
|
||||
spawn(5 SECONDS)
|
||||
if(holder && holder.my_atom)
|
||||
@@ -1133,7 +1133,7 @@
|
||||
required = /obj/item/slime_extract/bluespace
|
||||
|
||||
/decl/chemical_reaction/instant/slime/bluespace_chaotic_tele/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Bluespace extract reaction (chaos teleport) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Bluespace extract reaction (chaos teleport) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
for(var/mob/living/M in range(2,get_turf(holder.my_atom)))
|
||||
if(M.buckled)
|
||||
M.buckled.unbuckle_mob()
|
||||
@@ -1520,7 +1520,7 @@
|
||||
required = /obj/item/slime_extract/emerald
|
||||
|
||||
/decl/chemical_reaction/instant/slime/emerald_hell/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Emerald extract reaction (slip hell) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Emerald extract reaction (slip hell) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
for(var/turf/simulated/T in trange(5, get_turf(holder.my_atom)))
|
||||
if(!istype(T))
|
||||
continue
|
||||
|
||||
@@ -223,8 +223,8 @@
|
||||
visible_message(span_warning("Whirrs and spouts, starting to heat up!"))
|
||||
playsound(src, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 50, 1)
|
||||
|
||||
message_admins("[src] attempted to create an EX donk pocket at [x], [y], [z], last touched by [fingerprintslast]")
|
||||
log_game("[src] attempted to create an EX donk pocket at [x], [y], [z], last touched by [fingerprintslast]. (<A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)", 1)
|
||||
message_admins("[src] attempted to create an EX donk pocket at [x], [y], [z], last touched by [forensic_data?.get_lastprint()]")
|
||||
log_game("[src] attempted to create an EX donk pocket at [x], [y], [z], last touched by [forensic_data?.get_lastprint()]. (<A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)", 1)
|
||||
|
||||
sleep(6 SECONDS) // GET OUT, GET OUT
|
||||
stat = BROKEN
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/obj/item/clothing/shoes/apply_blood(var/image/standing)
|
||||
if(blood_DNA && blood_sprite_state && ishuman(loc))
|
||||
if(forensic_data?.has_blooddna() && blood_sprite_state && ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
var/image/bloodsies = image(icon = H.digitigrade ? 'icons/mob/human_races/masks/blood_digitigrade.dmi' : H.species.get_blood_mask(H), icon_state = blood_sprite_state)
|
||||
bloodsies.color = blood_color
|
||||
|
||||
@@ -136,10 +136,8 @@
|
||||
else
|
||||
var/obj/N = new /obj/item/clothing/mask/gas/half(src.loc)
|
||||
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
|
||||
N.fingerprints = src.fingerprints
|
||||
N.fingerprintshidden = src.fingerprintshidden
|
||||
N.fingerprintslast = src.fingerprintslast
|
||||
N.suit_fibers = src.suit_fibers
|
||||
transfer_fingerprints_to(N)
|
||||
transfer_fibres_to(N)
|
||||
if(!isturf(N.loc))
|
||||
user.put_in_hands(hailer)
|
||||
user.put_in_hands(N)
|
||||
|
||||
@@ -433,7 +433,7 @@
|
||||
required = /obj/item/slime_extract/nuclear
|
||||
|
||||
/decl/chemical_reaction/instant/slime/nuclear_radpulse/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Green extract reaction (radiation pulse) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Green extract reaction (radiation pulse) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
|
||||
holder.my_atom.visible_message(span_danger("\The [holder.my_atom] begins to vibrate violently!"))
|
||||
spawn(5 SECONDS)
|
||||
@@ -449,7 +449,7 @@
|
||||
required = /obj/item/slime_extract/green
|
||||
|
||||
/decl/chemical_reaction/instant/slime/green_emitter/on_reaction(var/datum/reagents/holder)
|
||||
log_and_message_admins("Green extract reaction (radiation emitter) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
log_and_message_admins("Green extract reaction (radiation emitter) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]")
|
||||
new /obj/item/slime_irradiator(get_turf(holder.my_atom))
|
||||
..()
|
||||
|
||||
|
||||
@@ -490,6 +490,7 @@
|
||||
#include "code\datums\datum.dm"
|
||||
#include "code\datums\datumvars.dm"
|
||||
#include "code\datums\EPv2.dm"
|
||||
#include "code\datums\forensics_crime.dm"
|
||||
#include "code\datums\ghost_query.dm"
|
||||
#include "code\datums\ghost_query_vr.dm"
|
||||
#include "code\datums\hierarchy.dm"
|
||||
|
||||
Reference in New Issue
Block a user