diff --git a/code/__DEFINES/cleaning.dm b/code/__DEFINES/cleaning.dm index 9f32992eb058..eed0ee5f5461 100644 --- a/code/__DEFINES/cleaning.dm +++ b/code/__DEFINES/cleaning.dm @@ -4,4 +4,9 @@ #define CLEAN_MEDIUM 3 // Acceptable tools #define CLEAN_STRONG 4 // Industrial strength #define CLEAN_IMPRESSIVE 5 // Cleaning strong enough your granny would be proud -#define CLEAN_GOD 6 // Cleans things spotless down to the atomic structure \ No newline at end of file +#define CLEAN_GOD 6 // Cleans things spotless down to the atomic structure + +//How strong things have to be to wipe forensic evidence... +#define CLEAN_STRENGTH_FINGERPRINTS CLEAN_IMPRESSIVE +#define CLEAN_STRENGTH_BLOOD CLEAN_WEAK +#define CLEAN_STRENGTH_FIBERS CLEAN_IMPRESSIVE diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 3912ed6a43bc..b5f48f821768 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -27,6 +27,13 @@ #define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called #define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human) #define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob) +#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //from base of atom/get_examine_name(): (/mob, list/overrides) + //Positions for overrides list + #define EXAMINE_POSITION_ARTICLE 1 + #define EXAMINE_POSITION_BEFORE 2 + #define EXAMINE_POSITION_NAME 3 + //End positions + #define COMPONENT_EXNAME_CHANGED 1 #define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (/atom/movable, /atom) #define COMSIG_ATOM_EX_ACT "atom_ex_act" //from base of atom/ex_act(): (severity, target) #define COMSIG_ATOM_EMP_ACT "atom_emp_act" //from base of atom/emp_act(): (severity) diff --git a/code/__DEFINES/forensics.dm b/code/__DEFINES/forensics.dm new file mode 100644 index 000000000000..bb512edcde62 --- /dev/null +++ b/code/__DEFINES/forensics.dm @@ -0,0 +1,2 @@ +#define IF_HAS_BLOOD_DNA(__thing) GET_COMPONENT_FROM(__FR##__thing, /datum/component/forensics, __thing); if(__FR##__thing && length(__FR##__thing.blood_DNA)) +#define IF_HAS_BLOOD_DNA_AND(__thing, __conditions...) GET_COMPONENT_FROM(__FR##__thing, /datum/component/forensics, __thing); if(__FR##__thing && length(__FR##__thing.blood_DNA) && (##__conditions)) diff --git a/code/datums/components/cleaning.dm b/code/datums/components/cleaning.dm index 5d9d5992e2a1..cb91d3c51353 100644 --- a/code/datums/components/cleaning.dm +++ b/code/datums/components/cleaning.dm @@ -13,28 +13,28 @@ if(!isturf(tile)) return - tile.clean_blood() + tile.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) for(var/A in tile) if(is_cleanable(A)) qdel(A) else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() + var/obj/item/I = A + I.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) else if(ishuman(A)) var/mob/living/carbon/human/cleaned_human = A if(cleaned_human.lying) if(cleaned_human.head) - cleaned_human.head.clean_blood() + cleaned_human.head.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) cleaned_human.update_inv_head() if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() + cleaned_human.wear_suit.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) cleaned_human.update_inv_wear_suit() else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() + cleaned_human.w_uniform.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) cleaned_human.update_inv_w_uniform() if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() + cleaned_human.shoes.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) cleaned_human.update_inv_shoes() - cleaned_human.clean_blood() + cleaned_human.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) cleaned_human.wash_cream() to_chat(cleaned_human, "[AM] cleans your face!") diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm index 4f5c024bd5a3..4ce0efda21e4 100644 --- a/code/datums/components/decal.dm +++ b/code/datums/components/decal.dm @@ -6,19 +6,11 @@ var/mutable_appearance/pic /datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_GOD, _color, _layer=TURF_LAYER, _description) - if(!isatom(parent) || !_icon || !_icon_state) + if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color)) . = COMPONENT_INCOMPATIBLE CRASH("A turf decal was applied incorrectly to [parent.type]: icon:[_icon ? _icon : "none"] icon_state:[_icon_state ? _icon_state : "none"]") - - // It has to be made from an image or dir breaks because of a byond bug - var/temp_image = image(_icon, null, _icon_state, _layer, _dir) - pic = new(temp_image) - pic.color = _color - - cleanable = _cleanable description = _description - - apply() + cleanable = _cleanable if(_dir) // If no dir is assigned at start then it follows the atom's dir RegisterSignal(COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react) @@ -26,6 +18,7 @@ RegisterSignal(COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react) if(_description) RegisterSignal(COMSIG_PARENT_EXAMINE, .proc/examine) + apply() /datum/component/decal/Destroy() remove() @@ -36,6 +29,15 @@ remove(thing) apply(thing) +/datum/component/decal/proc/generate_appearance(_icon, _icon_state, _dir, _layer, _color) + if(!_icon || !_icon_state) + return FALSE + // It has to be made from an image or dir breaks because of a byond bug + var/temp_image = image(_icon, null, _icon_state, _layer, _dir) + pic = new(temp_image) + pic.color = _color + return TRUE + /datum/component/decal/proc/apply(atom/thing) var/atom/master = thing || parent master.add_overlay(pic, TRUE) diff --git a/code/datums/components/decals/blood.dm b/code/datums/components/decals/blood.dm new file mode 100644 index 000000000000..f2dc9a48d0e8 --- /dev/null +++ b/code/datums/components/decals/blood.dm @@ -0,0 +1,35 @@ +/datum/component/decal/blood + dupe_mode = COMPONENT_DUPE_UNIQUE + +/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_STRENGTH_BLOOD, _color, _layer=ABOVE_OBJ_LAYER) + if(!isitem(parent)) + . = COMPONENT_INCOMPATIBLE + CRASH("Warning: Blood decal attempted to be added to non-item of type [parent.type]") + . = ..() + RegisterSignal(COMSIG_ATOM_GET_EXAMINE_NAME, .proc/get_examine_name) + +/datum/component/decal/blood/generate_appearance(_icon, _icon_state, _dir, _layer, _color) + var/obj/item/I = parent + if(!_icon) + _icon = 'icons/effects/blood.dmi' + if(!_icon_state) + _icon_state = "itemblood" + if(!initial(I.icon) || !initial(I.icon_state)) + return FALSE + var/static/list/blood_splatter_appearances = list() + //try to find a pre-processed blood-splatter. otherwise, make a new one + var/index = "[REF(initial(I.icon))]-[initial(I.icon_state)]" + pic = blood_splatter_appearances[index] + if(!pic) + var/icon/blood_splatter_icon = icon(initial(I.icon), initial(I.icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon(_icon, _icon_state), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + pic = mutable_appearance(blood_splatter_icon, initial(I.icon_state), I.layer) + blood_splatter_appearances[index] = pic + return TRUE + +/datum/component/decal/blood/proc/get_examine_name(mob/user, list/override) + var/atom/A = parent + override[EXAMINE_POSITION_ARTICLE] = A.gender == PLURAL? "some" : "a" + override[EXAMINE_POSITION_BEFORE] = " blood-stained " + return COMPONENT_EXNAME_CHANGED diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm new file mode 100644 index 000000000000..55633a20878f --- /dev/null +++ b/code/datums/components/forensics.dm @@ -0,0 +1,158 @@ +/datum/component/forensics + var/list/fingerprints //assoc print = print + var/list/hiddenprints //assoc ckey = realname/gloves/ckey + var/list/blood_DNA //assoc dna = bloodtype + var/list/fibers //assoc print = print + +/datum/component/forensics/InheritComponent(datum/component/forensics/F, original) //Use of | and |= being different here is INTENTIONAL. + fingerprints = fingerprints | F.fingerprints + hiddenprints = hiddenprints | F.hiddenprints + blood_DNA = blood_DNA | F.blood_DNA + fibers = fibers | F.fibers + check_blood() + return ..() + +/datum/component/forensics/Initialize(new_fingerprints, new_hiddenprints, new_blood_DNA, new_fibers) + if(!isatom(parent)) + . = COMPONENT_INCOMPATIBLE + CRASH("Forensics datum applied incorrectly to non-atom of type [parent.type]!") + fingerprints = new_fingerprints + hiddenprints = new_hiddenprints + blood_DNA = new_blood_DNA + fibers = new_fibers + check_blood() + RegisterSignal(COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_act) + +/datum/component/forensics/proc/wipe_fingerprints() + fingerprints = null + return TRUE + +/datum/component/forensics/proc/wipe_hiddenprints() + return //no. + +/datum/component/forensics/proc/wipe_blood_DNA() + blood_DNA = null + if(isitem(parent)) + qdel(parent.GetComponent(/datum/component/decal/blood)) + return TRUE + +/datum/component/forensics/proc/wipe_fibers() + fibers = null + return TRUE + +/datum/component/forensics/proc/clean_act(strength) + if(strength >= CLEAN_STRENGTH_FINGERPRINTS) + wipe_fingerprints() + if(strength >= CLEAN_STRENGTH_BLOOD) + wipe_blood_DNA() + if(strength >= CLEAN_STRENGTH_FIBERS) + wipe_fibers() + +/datum/component/forensics/proc/add_fingerprint_list(list/_fingerprints) //list(text) + if(!length(_fingerprints)) + return + LAZYINITLIST(fingerprints) + for(var/i in _fingerprints) //We use an associative list, make sure we don't just merge a non-associative list into ours. + fingerprints[i] = i + return TRUE + +/datum/component/forensics/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE) + if(!M) + return + add_hiddenprint(M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + add_fibers(H) + if(H.gloves) //Check if the gloves (if any) hide fingerprints + var/obj/item/clothing/gloves/G = H.gloves + if(G.transfer_prints) + ignoregloves = TRUE + if(!ignoregloves) + H.gloves.add_fingerprint(H, TRUE) //ignoregloves = 1 to avoid infinite loop. + return + var/full_print = md5(H.dna.uni_identity) + LAZYSET(fingerprints, full_print, full_print) + return TRUE + +/datum/component/forensics/proc/add_fiber_list(list/_fibertext) //list(text) + if(!length(_fibertext)) + return + LAZYINITLIST(fibers) + for(var/i in _fibertext) //We use an associative list, make sure we don't just merge a non-associative list into ours. + fibers[i] = i + return TRUE + +/datum/component/forensics/proc/add_fibers(mob/living/carbon/human/M) + var/fibertext + var/item_multiplier = isitem(src)?1.2:1 + if(M.wear_suit) + fibertext = "Material from \a [M.wear_suit]." + if(prob(10*item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + if(!(M.wear_suit.body_parts_covered & CHEST)) + if(M.w_uniform) + fibertext = "Fibers from \a [M.w_uniform]." + if(prob(12*item_multiplier) && !LAZYACCESS(fibers, fibertext)) //Wearing a suit means less of the uniform exposed. + LAZYSET(fibers, fibertext, fibertext) + if(!(M.wear_suit.body_parts_covered & HANDS)) + if(M.gloves) + fibertext = "Material from a pair of [M.gloves.name]." + if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + else if(M.w_uniform) + fibertext = "Fibers from \a [M.w_uniform]." + if(prob(15*item_multiplier) && !LAZYACCESS(fibers, fibertext)) + // "Added fibertext: [fibertext]" + LAZYSET(fibers, fibertext, fibertext) + if(M.gloves) + fibertext = "Material from a pair of [M.gloves.name]." + if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + else if(M.gloves) + fibertext = "Material from a pair of [M.gloves.name]." + if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext)) + LAZYSET(fibers, fibertext, fibertext) + return TRUE + +/datum/component/forensics/proc/add_hiddenprint_list(list/_hiddenprints) //list(ckey = text) + if(!length(_hiddenprints)) + return + LAZYINITLIST(hiddenprints) + for(var/i in _hiddenprints) //We use an associative list, make sure we don't just merge a non-associative list into ours. + hiddenprints[i] = _hiddenprints[i] + return TRUE + +/datum/component/forensics/proc/add_hiddenprint(mob/living/M) + if(!M || !M.key) + return + var/hasgloves = "" + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.gloves) + hasgloves = "(gloves)" + var/current_time = time_stamp() + if(!LAZYACCESS(hiddenprints, M.key)) + LAZYSET(hiddenprints, M.key, "First: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]") + else + var/laststamppos = findtext(LAZYACCESS(hiddenprints, M.key), " Last: ") + if(laststamppos) + LAZYSET(hiddenprints, M.key, copytext(hiddenprints[M.key], 1, laststamppos)) + hiddenprints[M.key] += " Last: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]" //made sure to be existing by if(!LAZYACCESS);else + fingerprintslast = M.ckey + return TRUE + +/datum/component/forensics/proc/add_blood_DNA(list/dna) //list(dna_enzymes = type) + if(!length(dna)) + return + LAZYINITLIST(blood_DNA) + for(var/i in dna) + blood_DNA[i] = dna[i] + check_blood() + return TRUE + +/datum/component/forensics/proc/check_blood() + if(!isitem(parent)) + return + if(!length(blood_DNA)) + return + parent.LoadComponent(/datum/component/decal/blood) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 211f9dea5974..67e5697916c0 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -5,10 +5,6 @@ var/flags_1 = 0 var/flags_2 = 0 - - var/list/fingerprints - var/list/fingerprintshidden - var/list/blood_DNA var/container_type = NONE var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. var/datum/reagents/reagents = null @@ -232,21 +228,22 @@ /atom/proc/in_contents_of(container)//can take class or object instance as argument if(ispath(container)) if(istype(src.loc, container)) - return 1 + return TRUE else if(src in container) - return 1 + return TRUE + return FALSE + +/atom/proc/get_examine_name(mob/user) + . = "\a [src]" + var/list/override = list(gender == PLURAL? "some" : "a" , " ", "[name]") + if(SendSignal(COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED) + . = override.Join("") + +/atom/proc/get_examine_string(mob/user, thats = FALSE) + . = "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" /atom/proc/examine(mob/user) - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - f_name += "blood-stained [name]!" - - to_chat(user, "[icon2html(src, user)] That's [f_name]") + to_chat(user, get_examine_string(user, TRUE)) if(desc) to_chat(user, desc) @@ -303,11 +300,6 @@ if(AM && isturf(AM.loc)) step(AM, turn(AM.dir, 180)) -GLOBAL_LIST_EMPTY(blood_splatter_icons) - -/atom/proc/blood_splatter_index() - return "[REF(initial(icon))]-[initial(icon_state)]" - //returns the mob's dna info as a list, to be inserted in an object's blood_DNA list /mob/living/proc/get_blood_dna_list() if(get_blood_id() != "blood") @@ -332,100 +324,28 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) // Returns 0 if we have that blood already var/new_blood_dna = L.get_blood_dna_list() if(!new_blood_dna) - return 0 - if(!blood_DNA) //if our list of DNA doesn't exist yet, initialise it. - blood_DNA = list() - var/old_length = blood_DNA.len - blood_DNA |= new_blood_dna - if(blood_DNA.len == old_length) - return 0 - return 1 - -//to add blood dna info to the object's blood_DNA list -/atom/proc/transfer_blood_dna(list/blood_dna) - if(!blood_DNA) - blood_DNA = list() - var/old_length = blood_DNA.len - blood_DNA |= blood_dna - if(blood_DNA.len > old_length) - return 1//some new blood DNA was added - + return FALSE + var/old_length = blood_DNA_length() + add_blood_DNA(new_blood_dna) + if(blood_DNA_length() == old_length) + return FALSE + return TRUE //to add blood from a mob onto something, and transfer their dna info /atom/proc/add_mob_blood(mob/living/M) var/list/blood_dna = M.get_blood_dna_list() if(!blood_dna) - return 0 - return add_blood(blood_dna) - -//to add blood onto something, with blood dna info to include. -/atom/proc/add_blood(list/blood_dna) - return 0 - -/obj/add_blood(list/blood_dna) - return transfer_blood_dna(blood_dna) - -/obj/item/add_blood(list/blood_dna) - var/blood_count = !blood_DNA ? 0 : blood_DNA.len - if(!..()) - return 0 - if(!blood_count)//apply the blood-splatter overlay if it isn't already in there - add_blood_overlay() - return 1 //we applied blood to the item - -/obj/item/proc/add_blood_overlay() - if(initial(icon) && initial(icon_state)) - //try to find a pre-processed blood-splatter. otherwise, make a new one - var/index = blood_splatter_index() - var/icon/blood_splatter_icon = GLOB.blood_splatter_icons[index] - if(!blood_splatter_icon) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon = fcopy_rsc(blood_splatter_icon) - GLOB.blood_splatter_icons[index] = blood_splatter_icon - add_overlay(blood_splatter_icon) - -/obj/item/clothing/gloves/add_blood(list/blood_dna) - . = ..() - transfer_blood = rand(2, 4) - -/turf/add_blood(list/blood_dna, list/datum/disease/diseases) - var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src - if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) - B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. - return 1 //we bloodied the floor - -/mob/living/carbon/human/add_blood(list/blood_dna) - if(wear_suit) - wear_suit.add_blood(blood_dna) - update_inv_wear_suit() - else if(w_uniform) - w_uniform.add_blood(blood_dna) - update_inv_w_uniform() - if(gloves) - var/obj/item/clothing/gloves/G = gloves - G.add_blood(blood_dna) - else - transfer_blood_dna(blood_dna) - bloody_hands = rand(2, 4) - update_inv_gloves() //handles bloody hands overlays and updating - return 1 - -/atom/proc/clean_blood() - if(islist(blood_DNA)) - blood_DNA = null - return 1 + return FALSE + return add_blood_DNA(blood_dna) /atom/proc/wash_cream() - return 1 + return TRUE /atom/proc/isinspace() if(isspaceturf(get_turf(src))) - return 1 + return TRUE else - return 0 + return FALSE /atom/proc/handle_fall() return diff --git a/code/game/gamemodes/devil/true_devil/_true_devil.dm b/code/game/gamemodes/devil/true_devil/_true_devil.dm index 10f0b3393f85..c1197a574260 100644 --- a/code/game/gamemodes/devil/true_devil/_true_devil.dm +++ b/code/game/gamemodes/devil/true_devil/_true_devil.dm @@ -67,10 +67,7 @@ //Left hand items for(var/obj/item/I in held_items) if(!(I.flags_1 & ABSTRACT_1)) - if(I.blood_DNA) - msg += "It is holding [icon2html(I, user)] [I.gender==PLURAL?"some":"a"] blood-stained [I.name] in its [get_held_index_name(get_held_index_of_item(I))]!\n" - else - msg += "It is holding [icon2html(I, user)] \a [I] in its [get_held_index_name(get_held_index_of_item(I))].\n" + msg += "It is holding [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))].\n" //Braindead if(!client && stat != DEAD) diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 21de0fc2f9ce..6c662fbc18b8 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -213,7 +213,7 @@ righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' var/mob/living/carbon/human/target = null var/list/mob/living/carbon/human/possible = list() - var/obj/item/linked_item = null + var/obj/item/voodoo_link = null var/cooldown_time = 30 //3s var/cooldown = 0 max_integrity = 10 @@ -237,10 +237,10 @@ cooldown = world.time +cooldown_time return - if(!linked_item) + if(!voodoo_link) if(I.loc == user && istype(I) && I.w_class <= WEIGHT_CLASS_SMALL) if (user.transferItemToLoc(I,src)) - linked_item = I + voodoo_link = I to_chat(user, "You attach [I] to the doll.") update_targets() @@ -255,11 +255,11 @@ return if(user.zone_selected == "chest") - if(linked_item) + if(voodoo_link) target = null - linked_item.forceMove(drop_location()) - to_chat(user, "You remove the [linked_item] from the doll.") - linked_item = null + voodoo_link.forceMove(drop_location()) + to_chat(user, "You remove the [voodoo_link] from the doll.") + voodoo_link = null update_targets() return @@ -291,10 +291,13 @@ /obj/item/voodoo/proc/update_targets() possible = list() - if(!linked_item) + if(!voodoo_link) return + var/list/prints = voodoo_link.return_fingerprints() + if(!length(prints)) + return FALSE for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - if(md5(H.dna.uni_identity) in linked_item.fingerprints) + if(prints[md5(H.dna.uni_identity)]) possible |= H /obj/item/voodoo/proc/GiveHint(mob/victim,force=0) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 7831437dc2d6..41f093a6d2b9 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -121,7 +121,7 @@ to_chat(user, "You remove the glass panel.") state = 3 icon_state = "3" - var/obj/item/stack/sheet/glass/G = new (drop_location(), 2) + var/obj/item/stack/sheet/glass/G = new(drop_location(), 2) G.add_fingerprint(user) return if(istype(P, /obj/item/screwdriver)) diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index a67ce24c9c7e..f3d393342883 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -154,11 +154,8 @@ to_chat(usr, "There's not enough room to build that here!") qdel(C) return - - if(href_list["dir"]) C.setDir(text2num(href_list["dir"])) - C.add_fingerprint(usr) C.update_icon() wait = world.time + 15 diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 53c0b746bd88..307107d51725 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -236,8 +236,7 @@ visible_message("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.") playsound(src, 'sound/machines/airlockclose.ogg', 25, 1) for(var/obj/item/I in src) //Scorches away blood and forensic evidence, although the SSU itself is unaffected - I.clean_blood() - I.fingerprints = list() + I.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRONG) var/datum/component/radioactive/contamination = I.GetComponent(/datum/component/radioactive) if(contamination) qdel(contamination) diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index f1384c0ea330..b48f14e50842 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -12,6 +12,10 @@ var/obj/item/color_source var/max_wash_capacity = 5 +/obj/machinery/washing_machine/ComponentInitialize() + . = ..() + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) + /obj/machinery/washing_machine/examine(mob/user) ..() to_chat(user, "Alt-click it to start a wash cycle.") @@ -36,20 +40,17 @@ busy = TRUE update_icon() - sleep(200) - wash_cycle() + addtimer(CALLBACK(src, .proc/wash_cycle), 200) -/obj/machinery/washing_machine/clean_blood() - ..() +/obj/machinery/washing_machine/proc/clean_blood() if(!busy) - bloody_mess = 0 + bloody_mess = FALSE update_icon() - /obj/machinery/washing_machine/proc/wash_cycle() for(var/X in contents) var/atom/movable/AM = X - AM.clean_blood() + AM.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) AM.machine_wash(src) busy = FALSE diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index f06525863dd2..d6b70604af4d 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -79,14 +79,11 @@ add_blood = bloodiness bloodiness -= add_blood S.bloody_shoes[blood_state] = min(MAX_SHOE_BLOODINESS,S.bloody_shoes[blood_state]+add_blood) - if(blood_DNA && blood_DNA.len) - S.add_blood(blood_DNA) + S.add_blood_DNA(return_blood_DNA()) S.blood_state = blood_state update_icon() H.update_inv_shoes() - - /obj/effect/decal/cleanable/proc/can_bloodcrawl_in() if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY)) return bloodiness diff --git a/code/game/objects/effects/decals/cleanable/aliens.dm b/code/game/objects/effects/decals/cleanable/aliens.dm index 333da7f48acb..55d5d32ffca6 100644 --- a/code/game/objects/effects/decals/cleanable/aliens.dm +++ b/code/game/objects/effects/decals/cleanable/aliens.dm @@ -6,10 +6,13 @@ icon = 'icons/effects/blood.dmi' icon_state = "xfloor1" random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") - blood_DNA = list("UNKNOWN DNA" = "X*") bloodiness = MAX_SHOE_BLOODINESS blood_state = BLOOD_STATE_XENO +/obj/effect/decal/cleanable/xenoblood/Initialize() + . = ..() + add_blood_DNA(list("UNKNOWN DNA" = "X*")) + /obj/effect/decal/cleanable/xenoblood/xsplatter random_icon_states = list("xgibbl1", "xgibbl2", "xgibbl3", "xgibbl4", "xgibbl5") @@ -62,4 +65,7 @@ /obj/effect/decal/cleanable/blood/xtracks icon_state = "xtracks" random_icon_states = null - blood_DNA = list("UNKNOWN DNA" = "X*") + +/obj/effect/decal/cleanable/blood/xtracks/Initialize() + . = ..() + add_blood_DNA(list("Unknown DNA" = "X*")) diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 42aec1582edd..fbcf22fb9090 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -4,13 +4,11 @@ icon = 'icons/effects/blood.dmi' icon_state = "floor1" random_icon_states = list("floor1", "floor2", "floor3", "floor4", "floor5", "floor6", "floor7") - blood_DNA = list() blood_state = BLOOD_STATE_HUMAN bloodiness = MAX_SHOE_BLOODINESS /obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C) - if (C.blood_DNA) - blood_DNA |= C.blood_DNA.Copy() + add_blood_DNA(C.return_blood_DNA()) ..() /obj/effect/decal/cleanable/blood/old @@ -21,7 +19,7 @@ /obj/effect/decal/cleanable/blood/old/Initialize(mapload, list/datum/disease/diseases) . = ..() icon_state += "-old" //This IS necessary because the parent /blood type uses icon randomization. - blood_DNA["Non-human DNA"] = "A+" + add_blood_DNA(list("Non-human DNA" = "A+")) /obj/effect/decal/cleanable/blood/splatter random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5") @@ -37,11 +35,9 @@ desc = "Your instincts say you shouldn't be following these." random_icon_states = null var/list/existing_dirs = list() - blood_DNA = list() /obj/effect/decal/cleanable/trail_holder/can_bloodcrawl_in() - return 1 - + return TRUE /obj/effect/decal/cleanable/blood/gibs name = "gibs" @@ -100,8 +96,7 @@ . = ..() setDir(pick(1,2,4,8)) icon_state += "-old" - blood_DNA["Non-human DNA"] = "A+" - + add_blood_DNA(list("Non-human DNA" = "A+")) /obj/effect/decal/cleanable/blood/drip name = "drips of blood" @@ -111,9 +106,8 @@ bloodiness = 0 var/drips = 1 - /obj/effect/decal/cleanable/blood/drip/can_bloodcrawl_in() - return 1 + return TRUE //BLOODY FOOTPRINTS @@ -151,7 +145,7 @@ if (!(exited_dirs & H.dir)) exited_dirs |= H.dir update_icon() - + /obj/effect/decal/cleanable/blood/footprints/update_icon() cut_overlays() diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index bd6e4d934b9c..e99a2fcbc697 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -30,9 +30,9 @@ digester.stomach_contents += gib if(MobDNA) - gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.blood_type + else if(istype(src, /obj/effect/gibspawner/generic)) // Probably a monkey - gib.blood_DNA["Non-human DNA"] = "A+" + gib.add_blood_DNA(list("Non-human DNA" = "A+")) var/list/directions = gibdirections[i] if(isturf(loc)) if(directions.len) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 1f0cc16252d3..131d51de907c 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -559,20 +559,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) M.become_blind(EYE_DAMAGE) to_chat(M, "You go blind!") -/obj/item/clean_blood() - . = ..() - if(.) - if(initial(icon) && initial(icon_state)) - var/index = blood_splatter_index() - var/icon/blood_splatter_icon = GLOB.blood_splatter_icons[index] - if(blood_splatter_icon) - cut_overlay(blood_splatter_icon) - -/obj/item/clothing/gloves/clean_blood() - . = ..() - if(.) - transfer_blood = 0 - /obj/item/singularity_pull(S, current_size) ..() if(current_size >= STAGE_FOUR) diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index f2a804e9e962..e9ccda03eacb 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -86,7 +86,7 @@ var/obj/effect/decal/cleanable/C = locate() in target qdel(C) target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - target.clean_blood() + target.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) target.wash_cream() return diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 9ba27af7ce83..77d1795b6b77 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -141,8 +141,8 @@ else icon_state = initial(icon_state) -/obj/item/device/radio/intercom/add_blood(list/blood_dna) - return 0 +/obj/item/device/radio/intercom/add_blood_DNA(list/blood_dna) + return FALSE //Created through the autolathe or through deconstructing intercoms. Can be applied to wall to make a new intercom on it! /obj/item/wallframe/intercom diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 6eaf3f08bd26..146bcbb3c3f7 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -20,8 +20,8 @@ user.visible_message("[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku!") return (BRUTELOSS|FIRELOSS) -/obj/item/melee/transforming/energy/add_blood(list/blood_dna) - return 0 +/obj/item/melee/transforming/energy/add_blood_DNA(list/blood_dna) + return FALSE /obj/item/melee/transforming/energy/is_sharp() return active * sharpness diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 874ffee050c3..3f1b5cd54ec5 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -285,8 +285,8 @@ shard.Consume() T.CalculateAdjacentTurfs() -/obj/item/melee/supermatter_sword/add_blood(list/blood_dna) - return 0 +/obj/item/melee/supermatter_sword/add_blood_DNA(list/blood_dna) + return FALSE /obj/item/melee/curator_whip name = "curator's whip" diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm index 62a354995b5a..19e8560d4d5d 100644 --- a/code/game/objects/items/mop.dm +++ b/code/game/objects/items/mop.dm @@ -25,7 +25,6 @@ /obj/item/mop/proc/clean(turf/A) if(reagents.has_reagent("water", 1) || reagents.has_reagent("holywater", 1) || reagents.has_reagent("vodka", 1) || reagents.has_reagent("cleaner", 1)) - A.clean_blood() A.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM) for(var/obj/effect/O in A) if(is_cleanable(O)) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index cef25a0a363c..7463c69b4d5e 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -336,10 +336,10 @@ else . = ..() -/obj/item/stack/proc/copy_evidences(obj/item/stack/from as obj) - blood_DNA = from.blood_DNA - fingerprints = from.fingerprints - fingerprintshidden = from.fingerprintshidden +/obj/item/stack/proc/copy_evidences(obj/item/stack/from) + add_blood_DNA(from.return_blood_DNA()) + add_fingerprint_list(from.return_fingerprints()) + add_hiddenprint_list(from.return_hiddenprints()) fingerprintslast = from.fingerprintslast //TODO bloody overlay diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm index 83fc74a46533..25b123e75c17 100644 --- a/code/game/objects/items/storage/book.dm +++ b/code/game/objects/items/storage/book.dm @@ -213,5 +213,5 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible", else return ..(M,user,heal_mode = FALSE) -/obj/item/storage/book/bible/syndicate/add_blood(list/blood_dna) +/obj/item/storage/book/bible/syndicate/add_blood_DNA(list/blood_dna) return FALSE diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index b7b48ff0431e..539c3c70a065 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -293,7 +293,7 @@ icon_state = "dualsaber[item_color][wielded]" else icon_state = "dualsaber0" - clean_blood()//blood overlays get weird otherwise, because the sprite changes. + SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) /obj/item/twohanded/dualsaber/attack(mob/target, mob/living/carbon/human/user) if(user.has_dna()) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index e5407175347a..c7595f39816a 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -296,8 +296,7 @@ /obj/machinery/shower/proc/wash_obj(obj/O) - O.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK) - . = O.clean_blood() + . = O.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK) O.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) if(isitem(O)) var/obj/item/I = O @@ -310,7 +309,6 @@ var/turf/tile = loc tile.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK) tile.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - tile.clean_blood() for(var/obj/effect/E in tile) if(is_cleanable(E)) qdel(E) @@ -361,7 +359,7 @@ else if(H.w_uniform && wash_obj(H.w_uniform)) H.update_inv_w_uniform() if(washgloves) - H.clean_blood() + H.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) if(H.shoes && washshoes && wash_obj(H.shoes)) H.update_inv_shoes() if(H.wear_mask && washmask && wash_obj(H.wear_mask)) @@ -378,9 +376,9 @@ else if(M.wear_mask && wash_obj(M.wear_mask)) M.update_inv_wear_mask(0) - M.clean_blood() + M.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) else - L.clean_blood() + L.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) /obj/machinery/shower/proc/contamination_cleanse(atom/movable/thing) var/datum/component/radioactive/healthy_green_glow = thing.GetComponent(/datum/component/radioactive) @@ -473,8 +471,7 @@ H.regenerate_icons() user.drowsyness = max(user.drowsyness - rand(2,3), 0) //Washing your face wakes you up if you're falling asleep else - user.clean_blood() - + user.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) /obj/structure/sink/attackby(obj/item/O, mob/living/user, params) if(busy) @@ -530,7 +527,7 @@ busy = FALSE return 1 busy = FALSE - O.clean_blood() + O.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) O.acid_level = 0 create_reagents(5) reagents.add_reagent(dispensedreagent, 5) diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index 6fed77b5fed3..463feeb30567 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -173,7 +173,7 @@ for(var/mob/living/simple_animal/slime/M in src) M.apply_water() - clean_blood() + SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK) for(var/obj/effect/O in src) if(is_cleanable(O)) qdel(O) diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index c230b142953e..39767234e51b 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -11,13 +11,21 @@ strip_delay = 20 equip_delay_other = 40 +/obj/item/clothing/gloves/ComponentInitialize() + . = ..() + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) + +/obj/item/clothing/gloves/proc/clean_blood(strength) + if(strength < CLEAN_STRENGTH_BLOOD) + return + transfer_blood = 0 /obj/item/clothing/gloves/worn_overlays(isinhands = FALSE) . = list() if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves") - if(blood_DNA) + IF_HAS_BLOOD_DNA(src) . += mutable_appearance('icons/effects/blood.dmi', "bloodyhands") /obj/item/clothing/gloves/update_clothes_damaged_state(damaging = TRUE) diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm index 8bc8f12decaf..9ec6542cf5a0 100644 --- a/code/modules/clothing/head/_head.dm +++ b/code/modules/clothing/head/_head.dm @@ -20,7 +20,7 @@ if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet") - if(blood_DNA) + IF_HAS_BLOOD_DNA(src) . += mutable_appearance('icons/effects/blood.dmi', "helmetblood") /obj/item/clothing/head/update_clothes_damaged_state(damaging = TRUE) diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm index eacfa3faea96..562375e897e1 100644 --- a/code/modules/clothing/masks/_masks.dm +++ b/code/modules/clothing/masks/_masks.dm @@ -15,7 +15,7 @@ if(body_parts_covered & HEAD) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask") - if(blood_DNA) + IF_HAS_BLOOD_DNA(src) . += mutable_appearance('icons/effects/blood.dmi', "maskblood") /obj/item/clothing/mask/update_clothes_damaged_state(damaging = TRUE) diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 2baaf911350e..b0f2a18a5b1b 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -12,7 +12,7 @@ if(body_parts_covered & HEAD) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask") - if(blood_DNA) + IF_HAS_BLOOD_DNA(src) . += mutable_appearance('icons/effects/blood.dmi', "maskblood") /obj/item/clothing/neck/tie diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index d71827df95f0..d058e82a3aa6 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -15,12 +15,16 @@ var/offset = 0 var/equipped_before_drop = FALSE +/obj/item/clothing/shoes/ComponentInitialize() + . = ..() + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) + /obj/item/clothing/shoes/worn_overlays(isinhands = FALSE) . = list() if(!isinhands) - var/bloody = 0 - if(blood_DNA) - bloody = 1 + var/bloody = FALSE + IF_HAS_BLOOD_DNA(src) + bloody = TRUE else bloody = bloody_shoes[BLOOD_STATE_HUMAN] @@ -53,8 +57,9 @@ var/mob/M = loc M.update_inv_shoes() -/obj/item/clothing/shoes/clean_blood() - ..() +/obj/item/clothing/shoes/proc/clean_blood(strength) + if(strength < CLEAN_STRENGTH_BLOOD) + return bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0) blood_state = BLOOD_STATE_NOT_BLOODY if(ismob(loc)) @@ -62,4 +67,4 @@ M.update_inv_shoes() /obj/item/proc/negates_gravity() - return 0 \ No newline at end of file + return FALSE \ No newline at end of file diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index 0068c1e10227..1ff8c65d4be2 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -14,7 +14,7 @@ if(!isinhands) if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damaged[blood_overlay_type]") - if(blood_DNA) + IF_HAS_BLOOD_DNA(src) . += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood") var/mob/living/carbon/human/M = loc if(ishuman(M) && M.w_uniform) diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index 43da19896b40..62fd5b5f5acb 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -19,10 +19,9 @@ /obj/item/clothing/under/worn_overlays(isinhands = FALSE) . = list() if(!isinhands) - if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform") - if(blood_DNA) + IF_HAS_BLOOD_DNA(src) . += mutable_appearance('icons/effects/blood.dmi', "uniformblood") if(accessory_overlay) . += accessory_overlay diff --git a/code/modules/detectivework/detective_work.dm b/code/modules/detectivework/detective_work.dm index 3eccbdcfbb27..5bf4ad27b89b 100644 --- a/code/modules/detectivework/detective_work.dm +++ b/code/modules/detectivework/detective_work.dm @@ -1,119 +1,102 @@ //CONTAINS: Suit fibers and Detective's Scanning Computer -/atom/var/list/suit_fibers +/atom/proc/return_fingerprints() + GET_COMPONENT(D, /datum/component/forensics) + if(D) + . = D.fingerprints -/atom/proc/add_fibers(mob/living/carbon/human/M) - if(M.gloves && istype(M.gloves, /obj/item/clothing/)) - var/obj/item/clothing/gloves/G = M.gloves - if(G.transfer_blood > 1) //bloodied gloves transfer blood to touched objects - if(add_blood(G.blood_DNA)) //only reduces the bloodiness of our gloves if the item wasn't already bloody - G.transfer_blood-- - else if(M.bloody_hands > 1) - if(add_blood(M.blood_DNA)) - M.bloody_hands-- - if(!suit_fibers) - suit_fibers = list() - var/fibertext - var/item_multiplier = isitem(src)?1.2:1 - if(M.wear_suit) - fibertext = "Material from \a [M.wear_suit]." - if(prob(10*item_multiplier) && !(fibertext in suit_fibers)) - suit_fibers += fibertext - if(!(M.wear_suit.body_parts_covered & CHEST)) - if(M.w_uniform) - fibertext = "Fibers from \a [M.w_uniform]." - if(prob(12*item_multiplier) && !(fibertext in suit_fibers)) //Wearing a suit means less of the uniform exposed. - suit_fibers += fibertext - if(!(M.wear_suit.body_parts_covered & HANDS)) - if(M.gloves) - fibertext = "Material from a pair of [M.gloves.name]." - if(prob(20*item_multiplier) && !(fibertext in suit_fibers)) - suit_fibers += fibertext - else if(M.w_uniform) - fibertext = "Fibers from \a [M.w_uniform]." - if(prob(15*item_multiplier) && !(fibertext in suit_fibers)) - // "Added fibertext: [fibertext]" - suit_fibers += fibertext - if(M.gloves) - 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]." - else if(M.gloves) - 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]." +/atom/proc/return_hiddenprints() + GET_COMPONENT(D, /datum/component/forensics) + if(D) + . = D.hiddenprints +/atom/proc/return_blood_DNA() + GET_COMPONENT(D, /datum/component/forensics) + if(D) + . = D.blood_DNA -/atom/proc/add_hiddenprint(mob/living/M) - if(!M || !M.key) - return +/atom/proc/blood_DNA_length() + GET_COMPONENT(D, /datum/component/forensics) + if(D) + . = length(D.blood_DNA) - if(!fingerprintshidden) //Add the list if it does not exist - fingerprintshidden = list() - - var/hasgloves = "" - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.gloves) - hasgloves = "(gloves)" - - var/current_time = time_stamp() - if(!fingerprintshidden[M.key]) - fingerprintshidden[M.key] = "First: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]" - else - var/laststamppos = findtext(fingerprintshidden[M.key], " Last: ") - if(laststamppos) - fingerprintshidden[M.key] = copytext(fingerprintshidden[M.key], 1, laststamppos) - fingerprintshidden[M.key] += " Last: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]" - - fingerprintslast = M.ckey +/atom/proc/return_fibers() + GET_COMPONENT(D, /datum/component/forensics) + if(D) + . = D.fibers +/atom/proc/add_fingerprint_list(list/fingerprints) //ASSOC LIST FINGERPRINT = FINGERPRINT + if(length(fingerprints)) + . = AddComponent(/datum/component/forensics, fingerprints) //Set ignoregloves to add prints irrespective of the mob having gloves on. -/atom/proc/add_fingerprint(mob/living/M, ignoregloves = 0) - if(!M || !M.key) - return +/atom/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE) + var/datum/component/forensics/D = AddComponent(/datum/component/forensics) + . = D.add_fingerprint(M, ignoregloves) - add_hiddenprint(M) +/atom/proc/add_fiber_list(list/fibertext) //ASSOC LIST FIBERTEXT = FIBERTEXT + if(length(fibertext)) + . = AddComponent(/datum/component/forensics, null, null, null, fibertext) - if(ishuman(M)) - var/mob/living/carbon/human/H = M +/atom/proc/add_fibers(mob/living/carbon/human/M) + var/old = 0 + if(M.gloves && istype(M.gloves, /obj/item/clothing)) + var/obj/item/clothing/gloves/G = M.gloves + old = length(G.return_blood_DNA()) + if(G.transfer_blood > 1) //bloodied gloves transfer blood to touched objects + if(add_blood_DNA(G.return_blood_DNA()) && length(G.return_blood_DNA()) > old) //only reduces the bloodiness of our gloves if the item wasn't already bloody + G.transfer_blood-- + else if(M.bloody_hands > 1) + old = length(M.return_blood_DNA()) + if(add_blood_DNA(M.return_blood_DNA()) && length(M.return_blood_DNA()) > old) + M.bloody_hands-- + var/datum/component/forensics/D = AddComponent(/datum/component/forensics) + . = D.add_fibers(M) - add_fibers(H) +/atom/proc/add_hiddenprint_list(list/hiddenprints) //NOTE: THIS IS FOR ADMINISTRATION FINGERPRINTS, YOU MUST CUSTOM SET THIS TO INCLUDE CKEY/REAL NAMES! CHECK FORENSICS.DM + if(length(hiddenprints)) + . = AddComponent(/datum/component/forensics, null, hiddenprints) - if(H.gloves) //Check if the gloves (if any) hide fingerprints - var/obj/item/clothing/gloves/G = H.gloves - if(G.transfer_prints) - ignoregloves = 1 +/atom/proc/add_hiddenprint(mob/living/M) + var/datum/component/forensics/D = AddComponent(/datum/component/forensics) + . = D.add_hiddenprint(M) - if(!ignoregloves) - H.gloves.add_fingerprint(H, 1) //ignoregloves = 1 to avoid infinite loop. - return +/atom/proc/add_blood_DNA(list/dna) //ASSOC LIST DNA = BLOODTYPE + return FALSE - if(!fingerprints) //Add the list if it does not exist - fingerprints = list() - var/full_print = md5(H.dna.uni_identity) - fingerprints[full_print] = full_print +/obj/add_blood_DNA(list/dna) + . = ..() + if(length(dna)) + . = AddComponent(/datum/component/forensics, null, null, dna) +/obj/item/clothing/gloves/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + . = ..() + transfer_blood = rand(2, 4) +/turf/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src + if(!B) + B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) + B.add_blood_DNA(blood_dna) //give blood info to the blood decal. + return TRUE //we bloodied the floor +/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + if(wear_suit) + wear_suit.add_blood_DNA(blood_dna) + update_inv_wear_suit() + else if(w_uniform) + w_uniform.add_blood_DNA(blood_dna) + update_inv_w_uniform() + if(gloves) + var/obj/item/clothing/gloves/G = gloves + G.add_blood_DNA(blood_dna) + else if(length(blood_dna)) + AddComponent(/datum/component/forensics, null, null, dna) + bloody_hands = rand(2, 4) + update_inv_gloves() //handles bloody hands overlays and updating + return TRUE /atom/proc/transfer_fingerprints_to(atom/A) - - // Make sure everything are lists. - if(!islist(A.fingerprints)) - A.fingerprints = list() - if(!islist(A.fingerprintshidden)) - A.fingerprintshidden = list() - - if(!islist(fingerprints)) - fingerprints = list() - if(!islist(fingerprintshidden)) - fingerprintshidden = list() - - // Transfer - if(fingerprints) - A.fingerprints |= fingerprints.Copy() //detective - if(fingerprintshidden) - A.fingerprintshidden |= fingerprintshidden.Copy() //admin + A.add_fingerprint_list(return_fingerprints()) + A.add_hiddenprint_list(return_hiddenprints()) A.fingerprintslast = fingerprintslast diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm index 1400fdbe0c06..9c98677291c8 100644 --- a/code/modules/detectivework/evidence.dm +++ b/code/modules/detectivework/evidence.dm @@ -23,7 +23,7 @@ icon_state = initial(icon_state) desc = initial(desc) -/obj/item/evidencebag/proc/evidencebagEquip(obj/item/I, mob/user) +/obj/item/evidencebag/proc/evidencebagEquip(obj/item/I, mob/user) if(!istype(I) || I.anchored == 1) return diff --git a/code/modules/detectivework/footprints_and_rag.dm b/code/modules/detectivework/footprints_and_rag.dm index 793805977c35..66405258b950 100644 --- a/code/modules/detectivework/footprints_and_rag.dm +++ b/code/modules/detectivework/footprints_and_rag.dm @@ -46,6 +46,5 @@ user.visible_message("[user] starts to wipe down [A] with [src]!", "You start to wipe down [A] with [src]...") if(do_after(user,30, target = A)) user.visible_message("[user] finishes wiping off the [A]!", "You finish wiping off the [A].") - A.clean_blood() - A.wash_cream() + A.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM) return diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index 8cb27ea4f32d..fa6c1f88cb93 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -67,20 +67,14 @@ //Make our lists var/list/fingerprints = list() - var/list/blood = list() - var/list/fibers = list() + var/list/blood = A.return_blood_DNA() + var/list/fibers = A.return_fibers() var/list/reagents = list() var/target_name = A.name // Start gathering - if(A.blood_DNA && A.blood_DNA.len) - blood = A.blood_DNA.Copy() - - if(A.suit_fibers && A.suit_fibers.len) - fibers = A.suit_fibers.Copy() - if(ishuman(A)) var/mob/living/carbon/human/H = A @@ -89,8 +83,7 @@ else if(!ismob(A)) - if(A.fingerprints && A.fingerprints.len) - fingerprints = A.fingerprints.Copy() + fingerprints = A.return_fingerprints() // Only get reagents from non-mobs. if(A.reagents && A.reagents.reagent_list.len) @@ -104,6 +97,7 @@ if(R.data["blood_DNA"] && R.data["blood_type"]) var/blood_DNA = R.data["blood_DNA"] var/blood_type = R.data["blood_type"] + LAZYINITLIST(blood) blood[blood_DNA] = blood_type // We gathered everything. Create a fork and slowly display the results to the holder of the scanner. @@ -112,7 +106,7 @@ add_log("[worldtime2text()][get_timestamp()] - [target_name]", 0) // Fingerprints - if(fingerprints && fingerprints.len) + if(length(fingerprints)) sleep(30) add_log("Prints:") for(var/finger in fingerprints) @@ -120,7 +114,7 @@ found_something = 1 // Blood - if (blood && blood.len) + if (length(blood)) sleep(30) add_log("Blood:") found_something = 1 @@ -128,7 +122,7 @@ add_log("Type: [blood[B]] DNA: [B]") //Fibers - if(fibers && fibers.len) + if(length(fibers)) sleep(30) add_log("Fibers:") for(var/fiber in fibers) @@ -136,7 +130,7 @@ found_something = 1 //Reagents - if(reagents && reagents.len) + if(length(reagents)) sleep(30) add_log("Reagents:") for(var/R in reagents) diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm index 15f76281ef83..c5336094f1d1 100644 --- a/code/modules/integrated_electronics/core/printer.dm +++ b/code/modules/integrated_electronics/core/printer.dm @@ -101,6 +101,7 @@ return if(..()) return TRUE + add_fingerprint(usr) if(href_list["category"]) current_category = href_list["category"] diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index fb5757c68e82..fd5250eb53ec 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -252,8 +252,7 @@ drop.transfer_mob_blood_dna(src) return else - temp_blood_DNA = list() - temp_blood_DNA |= drop.blood_DNA.Copy() //we transfer the dna from the drip to the splatter + temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter qdel(drop)//the drip is replaced by a bigger splatter else drop = new(T, get_static_viruses()) @@ -266,7 +265,7 @@ B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) B.transfer_mob_blood_dna(src) //give blood info to the blood decal. if(temp_blood_DNA) - B.blood_DNA |= temp_blood_DNA + B.add_blood_DNA(temp_blood_DNA) /mob/living/carbon/human/add_splatter_floor(turf/T, small_drip) if(!(NOBLOOD in dna.species.species_traits)) @@ -278,7 +277,7 @@ var/obj/effect/decal/cleanable/xenoblood/B = locate() in T.contents if(!B) B = new(T) - B.blood_DNA["UNKNOWN DNA"] = "X*" + B.add_blood_DNA(list("UNKNOWN DNA" = "X*")) /mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip) if(!T) diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 5ef14d5d09e8..99bb6d2250bf 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -11,21 +11,18 @@ if (handcuffed) msg += "[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!\n" if (head) - msg += "[t_He] [t_is] wearing [icon2html(head, user)] \a [src.head] on [t_his] head. \n" + msg += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head. \n" if (wear_mask) - msg += "[t_He] [t_is] wearing [icon2html(wear_mask, user)] \a [src.wear_mask] on [t_his] face.\n" + msg += "[t_He] [t_is] wearing [wear_mask.get_examine_string(user)] on [t_his] face.\n" if (wear_neck) - msg += "[t_He] [t_is] wearing [icon2html(wear_neck, user)] \a [src.wear_neck] around [t_his] neck.\n" + msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n" for(var/obj/item/I in held_items) if(!(I.flags_1 & ABSTRACT_1)) - if(I.blood_DNA) - msg += "[t_He] [t_is] holding [icon2html(I, user)] [I.gender==PLURAL?"some":"a"] blood-stained [I.name] in [t_his] [get_held_index_name(get_held_index_of_item(I))]!\n" - else - msg += "[t_He] [t_is] holding [icon2html(I, user)] \a [I] in [t_his] [get_held_index_name(get_held_index_of_item(I))].\n" + msg += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))].\n" if (back) - msg += "[t_He] [t_has] [icon2html(back, user)] \a [src.back] on [t_his] back.\n" + msg += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back.\n" var/appears_dead = 0 if (stat == DEAD) appears_dead = 1 diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 9b61b9675eb9..51b1241c03c4 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -21,54 +21,30 @@ if(U.attached_accessory) accessory_msg += " with [icon2html(U.attached_accessory, user)] \a [U.attached_accessory]" - if(w_uniform.blood_DNA) - msg += "[t_He] [t_is] wearing [icon2html(w_uniform, user)] [w_uniform.gender==PLURAL?"some":"a"] blood-stained [w_uniform.name][accessory_msg]!\n" - else - msg += "[t_He] [t_is] wearing [icon2html(w_uniform, user)] \a [w_uniform][accessory_msg].\n" - + msg += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_msg].\n" //head if(head) - if(head.blood_DNA) - msg += "[t_He] [t_is] wearing [icon2html(head, user)] [head.gender==PLURAL?"some":"a"] blood-stained [head.name] on [t_his] head!\n" - else - msg += "[t_He] [t_is] wearing [icon2html(head, user)] \a [head] on [t_his] head.\n" - + msg += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head.\n" //suit/armor if(wear_suit) - if(wear_suit.blood_DNA) - msg += "[t_He] [t_is] wearing [icon2html(wear_suit, user)] [wear_suit.gender==PLURAL?"some":"a"] blood-stained [wear_suit.name]!\n" - else - msg += "[t_He] [t_is] wearing [icon2html(wear_suit, user)] \a [wear_suit].\n" - + msg += "[t_He] [t_is] wearing [wear_suit.get_examine_string(user)].\n" //suit/armor storage if(s_store) - if(s_store.blood_DNA) - msg += "[t_He] [t_is] carrying [icon2html(s_store, user)] [s_store.gender==PLURAL?"some":"a"] blood-stained [s_store.name] on [t_his] [wear_suit.name]!\n" - else - msg += "[t_He] [t_is] carrying [icon2html(s_store, user)] \a [s_store] on [t_his] [wear_suit.name].\n" - + msg += "[t_He] [t_is] carrying [s_store.get_examine_string(user)] on [t_his] [wear_suit.name].\n" //back if(back) - if(back.blood_DNA) - msg += "[t_He] [t_has] [icon2html(back, user)] [back.gender==PLURAL?"some":"a"] blood-stained [back] on [t_his] back.\n" - else - msg += "[t_He] [t_has] [icon2html(back, user)] \a [back] on [t_his] back.\n" + msg += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back.\n" //Hands for(var/obj/item/I in held_items) if(!(I.flags_1 & ABSTRACT_1)) - if(I.blood_DNA) - msg += "[t_He] [t_is] holding [icon2html(I, user)] [I.gender==PLURAL?"some":"a"] blood-stained [I.name] in [t_his] [get_held_index_name(get_held_index_of_item(I))]!\n" - else - msg += "[t_He] [t_is] holding [icon2html(I, user)] \a [I] in [t_his] [get_held_index_name(get_held_index_of_item(I))].\n" + msg += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))].\n" + GET_COMPONENT(FR, /datum/component/forensics) //gloves if(gloves && !(slot_gloves in obscured)) - if(gloves.blood_DNA) - msg += "[t_He] [t_has] [icon2html(gloves, user)] [gloves.gender==PLURAL?"some":"a"] blood-stained [gloves.name] on [t_his] hands!\n" - else - msg += "[t_He] [t_has] [icon2html(gloves, user)] \a [gloves] on [t_his] hands.\n" - else if(blood_DNA) + msg += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands.\n" + else if(FR && length(FR.blood_DNA)) var/hand_number = get_num_arms() if(hand_number) msg += "[t_He] [t_has] [hand_number > 1 ? "" : "a"] blood-stained hand[hand_number > 1 ? "s" : ""]!\n" @@ -84,42 +60,30 @@ //belt if(belt) - if(belt.blood_DNA) - msg += "[t_He] [t_has] [icon2html(belt, user)] [belt.gender==PLURAL?"some":"a"] blood-stained [belt.name] about [t_his] waist!\n" - else - msg += "[t_He] [t_has] [icon2html(belt, user)] \a [belt] about [t_his] waist.\n" + msg += "[t_He] [t_has] [belt.get_examine_string(user)] about [t_his] waist.\n" //shoes if(shoes && !(slot_shoes in obscured)) - if(shoes.blood_DNA) - msg += "[t_He] [t_is] wearing [icon2html(shoes, user)] [shoes.gender==PLURAL?"some":"a"] blood-stained [shoes.name] on [t_his] feet!\n" - else - msg += "[t_He] [t_is] wearing [icon2html(shoes, user)] \a [shoes] on [t_his] feet.\n" + msg += "[t_He] [t_is] wearing [shoes.get_examine_string(user)] on [t_his] feet.\n" //mask if(wear_mask && !(slot_wear_mask in obscured)) - if(wear_mask.blood_DNA) - msg += "[t_He] [t_has] [icon2html(wear_mask, user)] [wear_mask.gender==PLURAL?"some":"a"] blood-stained [wear_mask.name] on [t_his] face!\n" - else - msg += "[t_He] [t_has] [icon2html(wear_mask, user)] \a [wear_mask] on [t_his] face.\n" + msg += "[t_He] [t_has] [wear_mask.get_examine_string(user)] on [t_his] face.\n" if (wear_neck && !(slot_neck in obscured)) - msg += "[t_He] [t_is] wearing [icon2html(wear_neck, user)] \a [src.wear_neck] around [t_his] neck.\n" + msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n" //eyes if(glasses && !(slot_glasses in obscured)) - if(glasses.blood_DNA) - msg += "[t_He] [t_has] [icon2html(glasses, user)] [glasses.gender==PLURAL?"some":"a"] blood-stained [glasses] covering [t_his] eyes!\n" - else - msg += "[t_He] [t_has] [icon2html(glasses, user)] \a [glasses] covering [t_his] eyes.\n" + msg += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes.\n" //ears if(ears && !(slot_ears in obscured)) - msg += "[t_He] [t_has] [icon2html(ears, user)] \a [ears] on [t_his] ears.\n" + msg += "[t_He] [t_has] [ears.get_examine_string(user)] on [t_his] ears.\n" //ID if(wear_id) - msg += "[t_He] [t_is] wearing [icon2html(wear_id, user)] \a [wear_id].\n" + msg += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)].\n" //Jitters switch(jitteriness) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 971bc2338c4e..e282310b82cd 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -25,6 +25,7 @@ create_internal_organs() //most of it is done in set_species now, this is only for parent call handcrafting = new() + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) . = ..() @@ -678,19 +679,18 @@ if(..()) dropItemToGround(I) -/mob/living/carbon/human/clean_blood() - var/mob/living/carbon/human/H = src - if(H.gloves) - if(H.gloves.clean_blood()) - H.update_inv_gloves() +/mob/living/carbon/human/proc/clean_blood(strength) + if(strength < CLEAN_STRENGTH_BLOOD) + return + if(gloves) + if(gloves.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)) + update_inv_gloves() else - ..() // Clear the Blood_DNA list - if(H.bloody_hands) - H.bloody_hands = 0 - H.update_inv_gloves() + if(bloody_hands) + bloody_hands = 0 + update_inv_gloves() update_icons() //apply the now updated overlays to the mob - /mob/living/carbon/human/wash_cream() if(creamed) //clean both to prevent a rare bug cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_lizard")) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 6e5d8c7373fa..ae78e1e58515 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -51,8 +51,7 @@ FP.blood_state = S.blood_state FP.entered_dirs |= dir FP.bloodiness = S.bloody_shoes[S.blood_state] - BLOOD_LOSS_IN_SPREAD - if(S.blood_DNA && S.blood_DNA.len) - FP.transfer_blood_dna(S.blood_DNA) + FP.add_blood_DNA(S.return_blood_DNA()) FP.update_icon() update_inv_shoes() //End bloody footprints diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 95e37c4df553..2b24a5303123 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -176,7 +176,8 @@ There are several things that need to be remembered: var/obj/screen/inventory/inv = hud_used.inv_slots[slot_gloves] inv.update_icon() - if(!gloves && blood_DNA) + GET_COMPONENT(FR, /datum/component/forensics) + if(!gloves && FR && length(FR.blood_DNA)) var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER) if(get_num_arms() < 2) if(has_left_hand()) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 98d455703750..917211736bad 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -472,8 +472,7 @@ if(isturf(next)) if(bloodiness) var/obj/effect/decal/cleanable/blood/tracks/B = new(loc) - if(blood_DNA && blood_DNA.len) - B.blood_DNA |= blood_DNA.Copy() + B.add_blood_DNA(return_blood_DNA()) var/newdir = get_dir(next, loc) if(newdir == dir) B.setDir(newdir) @@ -655,8 +654,7 @@ T.add_mob_blood(H) var/list/blood_dna = H.get_blood_dna_list() - if(blood_dna) - transfer_blood_dna(blood_dna) + add_blood_DNA(blood_dna) bloodiness += 4 // player on mulebot attempted to move diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index ea159a574976..4d45716983f9 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -177,24 +177,15 @@ //Hands for(var/obj/item/I in held_items) if(!(I.flags_1 & ABSTRACT_1)) - if(I.blood_DNA) - msg += "It has [icon2html(I, user)] [I.gender==PLURAL?"some":"a"] blood-stained [I.name] in its [get_held_index_name(get_held_index_of_item(I))]!\n" - else - msg += "It has [icon2html(I, user)] \a [I] in its [get_held_index_name(get_held_index_of_item(I))].\n" + msg += "It has [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))].\n" //Internal storage if(internal_storage && !(internal_storage.flags_1&ABSTRACT_1)) - if(internal_storage.blood_DNA) - msg += "It is holding [icon2html(internal_storage, user)] [internal_storage.gender==PLURAL?"some":"a"] blood-stained [internal_storage.name] in its internal storage!\n" - else - msg += "It is holding [icon2html(internal_storage, user)] \a [internal_storage] in its internal storage.\n" + msg += "It is holding [internal_storage.get_examine_string(user)] in its internal storage.\n" //Cosmetic hat - provides no function other than looks if(head && !(head.flags_1&ABSTRACT_1)) - if(head.blood_DNA) - msg += "It is wearing [icon2html(head, user)] [head.gender==PLURAL?"some":"a"] blood-stained [head.name] on its head!\n" - else - msg += "It is wearing [icon2html(head, user)] \a [head] on its head.\n" + msg += "It is wearing [head.get_examine_string(user)] on its head.\n" //Braindead if(!client && stat != DEAD) diff --git a/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm b/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm index 2f3ee4cefb33..f36a30ccaedd 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm @@ -31,16 +31,9 @@ for(var/obj/item/I in held_items) if(!(I.flags_1 & ABSTRACT_1)) - if(I.blood_DNA) - msg += "It has [icon2html(I, user)] [I.gender==PLURAL?"some":"a"] blood-stained [I.name] in its [get_held_index_name(get_held_index_of_item(I))]!\n" - else - msg += "It has [icon2html(I, user)] \a [I] in its [get_held_index_name(get_held_index_of_item(I))].\n" - + msg += "It has [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))].\n" if(internal_storage && !(internal_storage.flags_1&ABSTRACT_1)) - if(internal_storage.blood_DNA) - msg += "It is holding [icon2html(internal_storage, user)] [internal_storage.gender==PLURAL?"some":"a"] blood-stained [internal_storage.name] in its internal storage!\n" - else - msg += "It is holding [icon2html(internal_storage, user)] \a [internal_storage] in its internal storage.\n" + msg += "It is holding [internal_storage.get_examine_string(user)] in its internal storage.\n" msg += "*---------*" to_chat(user, msg) else diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index a993274d9061..57718459fc23 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -171,6 +171,6 @@ qdel(target) return TRUE var/atom/movable/M = target - M.clean_blood() + M.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) visible_message("[src] polishes \the [target].") return TRUE diff --git a/code/modules/ninja/suit/gloves.dm b/code/modules/ninja/suit/gloves.dm index c49ca072e80a..ef02a8a7929f 100644 --- a/code/modules/ninja/suit/gloves.dm +++ b/code/modules/ninja/suit/gloves.dm @@ -40,26 +40,26 @@ /obj/item/clothing/gloves/space_ninja/Touch(atom/A,proximity) if(!candrain || draining) - return 0 + return FALSE if(!ishuman(loc)) - return 0 //Only works while worn + return FALSE //Only works while worn var/mob/living/carbon/human/H = loc var/obj/item/clothing/suit/space/space_ninja/suit = H.wear_suit if(!istype(suit)) - return 0 + return FALSE if(isturf(A)) - return 0 + return FALSE if(!proximity) - return 0 + return FALSE A.add_fingerprint(H) - draining = 1 + draining = TRUE . = A.ninjadrain_act(suit,H,src) - draining = 0 + draining = FALSE if(isnum(.)) //Numerical values of drained handle their feedback here, Alpha values handle it themselves (Research hacking) if(.) @@ -67,7 +67,7 @@ else to_chat(H, "\The [A] has run dry of energy, you must find another source!") else - . = 0 //as to not cancel attack_hand() + . = FALSE //as to not cancel attack_hand() /obj/item/clothing/gloves/space_ninja/proc/toggledrain() diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index ca424cb80dd1..3ada84d60142 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -665,7 +665,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai NC.d1 = 0 NC.d2 = fdirn - NC.add_fingerprint() + NC.add_fingerprint(user) NC.update_icon() //create a new powernet with the cable, if needed it will be merged later @@ -716,7 +716,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai //updates the stored cable coil C.update_stored(2, item_color) - C.add_fingerprint() + C.add_fingerprint(user) C.update_icon() diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 252a9cd8c75c..865ed29a42c3 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -164,7 +164,7 @@ if(iscarbon(user)) var/mob/living/carbon/C = user user_dna = C.dna - B.add_blood(user_dna) + B.add_blood_DNA(user_dna) var/datum/callback/gibspawner = CALLBACK(GLOBAL_PROC, /proc/spawn_atom_to_turf, /obj/effect/gibspawner/generic, B, 1, FALSE, list(user_dna)) B.throw_at(target, BRAINS_BLOWN_THROW_RANGE, BRAINS_BLOWN_THROW_SPEED, callback=gibspawner) return(BRUTELOSS) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 219d9df8eff8..5c4cdcf7e0dd 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -79,8 +79,7 @@ if(!B) B = new(T) if(data["blood_DNA"]) - B.blood_DNA[data["blood_DNA"]] = data["blood_type"] - + B.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"])) /datum/reagent/liquidgibs name = "Liquid gibs" @@ -941,12 +940,12 @@ else if(O) O.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - O.clean_blood() + O.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) /datum/reagent/space_cleaner/reaction_turf(turf/T, reac_volume) if(reac_volume >= 1) T.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - T.clean_blood() + T.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) for(var/obj/effect/decal/cleanable/C in T) qdel(C) @@ -964,26 +963,26 @@ H.lip_style = null H.update_body() for(var/obj/item/I in C.held_items) - I.clean_blood() + I.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) if(C.wear_mask) - if(C.wear_mask.clean_blood()) + if(C.wear_mask.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)) C.update_inv_wear_mask() if(ishuman(M)) var/mob/living/carbon/human/H = C if(H.head) - if(H.head.clean_blood()) + if(H.head.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)) H.update_inv_head() if(H.wear_suit) - if(H.wear_suit.clean_blood()) + if(H.wear_suit.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)) H.update_inv_wear_suit() else if(H.w_uniform) - if(H.w_uniform.clean_blood()) + if(H.w_uniform.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)) H.update_inv_w_uniform() if(H.shoes) - if(H.shoes.clean_blood()) + if(H.shoes.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)) H.update_inv_shoes() H.wash_cream() - M.clean_blood() + M.SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD) /datum/reagent/space_cleaner/ez_clean name = "EZ Clean" diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm index 3c409960295c..216aea306ac5 100644 --- a/code/modules/shuttle/computer.dm +++ b/code/modules/shuttle/computer.dm @@ -14,6 +14,7 @@ if(..(user)) return add_fingerprint(usr) + var/list/options = params2list(possible_destinations) var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) var/dat = "Status: [M ? M.getStatusText() : "*Missing*"]

" diff --git a/tgstation.dme b/tgstation.dme index 5f90a04be826..79b40f67de2d 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -43,6 +43,7 @@ #include "code\__DEFINES\events.dm" #include "code\__DEFINES\flags.dm" #include "code\__DEFINES\food.dm" +#include "code\__DEFINES\forensics.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\integrated_electronics.dm" #include "code\__DEFINES\inventory.dm" @@ -311,6 +312,7 @@ #include "code\datums\components\chasm.dm" #include "code\datums\components\cleaning.dm" #include "code\datums\components\decal.dm" +#include "code\datums\components\forensics.dm" #include "code\datums\components\infective.dm" #include "code\datums\components\jousting.dm" #include "code\datums\components\knockoff.dm" @@ -325,6 +327,7 @@ #include "code\datums\components\spooky.dm" #include "code\datums\components\squeek.dm" #include "code\datums\components\thermite.dm" +#include "code\datums\components\decals\blood.dm" #include "code\datums\diseases\_disease.dm" #include "code\datums\diseases\_MobProcs.dm" #include "code\datums\diseases\anxiety.dm"