diff --git a/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm b/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm
index 18d050e276e..18694d3b4a0 100644
--- a/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm
+++ b/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm
@@ -1738,7 +1738,7 @@
},
/obj/item/ammo_casing/spent,
/obj/effect/decal/cleanable/xenoblood/xgibs{
- icon_state = "xtracks";
+ icon_state = "tracks";
dir = 1
},
/turf/open/floor/mineral/plastitanium,
diff --git a/_maps/modular_generic/beach_l_ribs.dmm b/_maps/modular_generic/beach_l_ribs.dmm
index ea0a2a9abd0..69992c2ed76 100644
--- a/_maps/modular_generic/beach_l_ribs.dmm
+++ b/_maps/modular_generic/beach_l_ribs.dmm
@@ -373,7 +373,7 @@
dir = 1
},
/obj/effect/decal/cleanable/xenoblood{
- icon_state = "xfloor4"
+ icon_state = "floor4"
},
/turf/open/misc/asteroid{
icon_state = "asteroid3"
@@ -523,7 +523,7 @@
"OJ" = (
/obj/structure/statue/bone/rib,
/obj/effect/decal/cleanable/xenoblood{
- icon_state = "xfloor5"
+ icon_state = "floor5"
},
/turf/open/misc/asteroid{
icon_state = "asteroid0"
@@ -541,7 +541,7 @@
"PS" = (
/obj/structure/statue/bone/rib,
/obj/effect/decal/cleanable/xenoblood{
- icon_state = "xfloor7"
+ icon_state = "floor7"
},
/obj/effect/turf_decal/trimline/dark/warning{
dir = 1
@@ -670,7 +670,7 @@
dir = 4
},
/obj/effect/decal/cleanable/xenoblood{
- icon_state = "xfloor5"
+ icon_state = "floor5"
},
/obj/effect/turf_decal/trimline/dark/warning{
dir = 6
diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index 7408294770e..ca6a1660a01 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -216,6 +216,12 @@
#define COLOR_SAMPLE_BROWN "#91542d"
#define COLOR_SAMPLE_GRAY "#5e5856"
+///Colors for blood greyscale sprites
+#define BLOOD_COLOR_RED "#FF291E" // This is lighter than the blood reagent itself because for the greyscale to look the same as before it needs to be the lightest parts of the sprite.
+#define BLOOD_COLOR_XENO "#D5FF2C"
+#define BLOOD_COLOR_OIL "#262626"
+#define BLOOD_COLOR_BLACK "#2C0903"
+
///Main colors for UI themes
#define COLOR_THEME_MIDNIGHT "#6086A0"
#define COLOR_THEME_PLASMAFIRE "#FFB200"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index b30583544b8..05b91d64754 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -32,6 +32,28 @@
/// Temperature at which blood loss and regen stops. [/mob/living/carbon/human/proc/handle_blood]
#define BLOOD_STOP_TEMP 225
+// Bloodtype defines
+#define BLOOD_TYPE_A_MINUS "A-"
+#define BLOOD_TYPE_A_PLUS "A+"
+#define BLOOD_TYPE_B_MINUS "B-"
+#define BLOOD_TYPE_B_PLUS "B+"
+#define BLOOD_TYPE_AB_MINUS "AB-"
+#define BLOOD_TYPE_AB_PLUS "AB+"
+#define BLOOD_TYPE_O_MINUS "O-"
+#define BLOOD_TYPE_O_PLUS "O+"
+#define BLOOD_TYPE_UNIVERSAL "U"
+#define BLOOD_TYPE_LIZARD "L"
+#define BLOOD_TYPE_VAMPIRE "V"
+#define BLOOD_TYPE_ANIMAL "Y-"
+#define BLOOD_TYPE_ETHEREAL "LE"
+#define BLOOD_TYPE_TOX "TOX"
+#define BLOOD_TYPE_OIL "Oil"
+#define BLOOD_TYPE_MEAT "MT-"
+#define BLOOD_TYPE_CLOWN "C"
+#define BLOOD_TYPE_XENO "X*"
+#define BLOOD_TYPE_H2O "H2O"
+#define BLOOD_TYPE_SNAIL "S"
+
//Sizes of mobs, used by mob/living/var/mob_size
#define MOB_SIZE_TINY 0
#define MOB_SIZE_SMALL 1
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index a6ba350eee3..243b0c2b79d 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -56,6 +56,8 @@
#define BODYPART_IMPLANTED (1<<2)
/// Bodypart never displays as a husk
#define BODYPART_UNHUSKABLE (1<<3)
+/// Bodypart has never been added to a mob
+#define BODYPART_VIRGIN (1<<4)
// Bodypart change blocking flags
///Bodypart does not get replaced during set_species()
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index e94d58dd693..d1ff5a4f63c 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -379,3 +379,6 @@ rough example of the "cone" made by the 3 dirs checked
"x" = icon_width > ICON_SIZE_X && pixel_x != 0 ? (icon_width - ICON_SIZE_X) * 0.5 : 0,
"y" = icon_height > ICON_SIZE_Y && pixel_y != 0 ? (icon_height - ICON_SIZE_Y) * 0.5 : 0,
)
+
+/// Helper for easily adding blood from INSIDE a mob to an atom (NOT blood ON the mob)
+#define add_mob_blood(from_who) add_blood_DNA(from_who.get_blood_dna_list())
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 9d0d91d8ce7..ee872c783f2 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -6,8 +6,19 @@
/// Two mobs one is facing a person, but the other is perpendicular
#define FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR 3 //Do I win the most informative but also most stupid define award?
-/proc/random_blood_type()
- return pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
+/// Returns one of the human blood types at random, weighted by their rarity
+/proc/random_human_blood_type()
+ return get_blood_type(pick_weight(
+ list(
+ BLOOD_TYPE_O_MINUS = 4,
+ BLOOD_TYPE_O_PLUS = 36,
+ BLOOD_TYPE_A_MINUS = 3,
+ BLOOD_TYPE_A_PLUS = 28,
+ BLOOD_TYPE_B_MINUS= 1,
+ BLOOD_TYPE_B_PLUS = 20,
+ BLOOD_TYPE_AB_MINUS = 1,
+ BLOOD_TYPE_AB_PLUS = 5,
+ )))
/proc/random_eye_color()
switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino"))
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index d88d08302ee..158e8ace8f0 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -114,6 +114,18 @@ GLOBAL_LIST_INIT(language_types_by_name, init_language_types_by_name())
lang_list[initial(lang_type.name)] = lang_type
return lang_list
+// A list of all the possible blood types, keyed by id (which is just the name in most cases)
+GLOBAL_LIST_INIT(blood_types, init_blood_types())
+
+/// Initializes the list of blood type singletons
+/proc/init_blood_types()
+ . = list()
+ for(var/datum/blood_type/blood_type_path as anything in subtypesof(/datum/blood_type))
+ if(blood_type_path::root_abstract_type == blood_type_path) // Don't instantiate abstract blood types
+ continue
+ var/datum/blood_type/new_type = new blood_type_path()
+ .[new_type.id] = new_type
+
/// An assoc list of species IDs to type paths
GLOBAL_LIST_INIT(species_list, init_species_list())
/// List of all species prototypes to reference, assoc [type] = prototype
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 73e2427f922..57c1af477d5 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -352,7 +352,21 @@
attacking_item.add_mob_blood(src)
add_splatter_floor(get_turf(src))
if(get_dist(attacker, src) <= 1)
- attacker.add_mob_blood(src)
+ if(ishuman(attacker))
+ var/bloodied_things = ITEM_SLOT_GLOVES
+ if(damage_done >= 20 || (damage_done >= 15 && prob(25)))
+ bloodied_things |= ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING
+ if(prob(33) && damage_done >= 10)
+ bloodied_things |= ITEM_SLOT_FEET
+ if(prob(33) && damage_done >= 24) // fireaxe damage, because heeeeere's johnny
+ bloodied_things |= ITEM_SLOT_MASK
+ if(prob(33) && damage_done >= 30) // esword damage
+ bloodied_things |= ITEM_SLOT_HEAD
+
+ var/mob/living/carbon/human/human_attacker = attacker
+ human_attacker.add_blood_DNA_to_items(get_blood_dna_list(), bloodied_things)
+ else
+ attacker.add_mob_blood(src)
return TRUE
return FALSE
@@ -369,15 +383,10 @@
switch(hit_zone)
if(BODY_ZONE_HEAD)
if(.)
- if(wear_mask)
- wear_mask.add_mob_blood(src)
- update_worn_mask()
- if(head)
- head.add_mob_blood(src)
- update_worn_head()
- if(glasses && prob(33))
- glasses.add_mob_blood(src)
- update_worn_glasses()
+ var/bloodied_things = ITEM_SLOT_MASK|ITEM_SLOT_HEAD
+ if(prob(33))
+ bloodied_things |= ITEM_SLOT_EYES
+ add_blood_DNA_to_items(get_blood_dna_list(), bloodied_things)
if(!attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_HEAD_INJURY_BLOCKED) && attacking_item.damtype == BRUTE)
if(prob(damage_done))
@@ -402,12 +411,7 @@
if(BODY_ZONE_CHEST)
if(.)
- if(wear_suit)
- wear_suit.add_mob_blood(src)
- update_worn_oversuit()
- if(w_uniform)
- w_uniform.add_mob_blood(src)
- update_worn_undersuit()
+ add_blood_DNA_to_items(get_blood_dna_list(), ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING)
if(stat == CONSCIOUS && !attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED) && attacking_item.damtype == BRUTE)
if(prob(damage_done))
diff --git a/code/datums/components/blood_walk.dm b/code/datums/components/blood_walk.dm
index 6bd6f304446..f6d8a07e289 100644
--- a/code/datums/components/blood_walk.dm
+++ b/code/datums/components/blood_walk.dm
@@ -26,6 +26,7 @@
target_dir_change = FALSE,
transfer_blood_dna = FALSE,
max_blood = INFINITY,
+ blood_dna_info = list("meaty DNA" = get_blood_type(BLOOD_TYPE_MEAT))
)
if(!ismovable(parent))
diff --git a/code/datums/components/bloody_spreader.dm b/code/datums/components/bloody_spreader.dm
index 823bc5e9507..ff9702f7133 100644
--- a/code/datums/components/bloody_spreader.dm
+++ b/code/datums/components/bloody_spreader.dm
@@ -32,8 +32,13 @@
/datum/component/bloody_spreader/proc/spread_yucky_blood(atom/parent, atom/bloody_fool)
SIGNAL_HANDLER
- bloody_fool.add_blood_DNA(blood_dna, diseases)
- blood_left--
+ if(ishuman(bloody_fool))
+ var/mob/living/carbon/human/bloody_fool_human = bloody_fool
+ bloody_fool_human.add_blood_DNA_to_items(blood_dna, ITEM_SLOT_GLOVES)
+ blood_left -= 3
+ else
+ bloody_fool.add_blood_DNA(blood_dna, diseases)
+ blood_left -= 1
if(blood_left <= 0)
qdel(src)
diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm
index 342e27c4b29..bdd8942f2eb 100644
--- a/code/datums/components/bloodysoles.dm
+++ b/code/datums/components/bloodysoles.dm
@@ -111,8 +111,17 @@
if(HAS_TRAIT(parent_atom, TRAIT_LIGHT_STEP) || (wielder && HAS_TRAIT(wielder, TRAIT_LIGHT_STEP))) //the character is agile enough to don't mess their clothing and hands just from one blood splatter at floor
return TRUE
- parent_atom.add_blood_DNA(GET_ATOM_BLOOD_DNA(pool))
- update_icon()
+ if(ishuman(parent))
+ var/bloody_slots = ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING|ITEM_SLOT_FEET
+ var/mob/living/carbon/human/to_bloody = parent
+ if(to_bloody.body_position == LYING_DOWN)
+ bloody_slots |= ITEM_SLOT_HEAD|ITEM_SLOT_MASK|ITEM_SLOT_GLOVES
+
+ to_bloody.add_blood_DNA_to_items(GET_ATOM_BLOOD_DNA(pool), bloody_slots)
+ return
+
+ var/atom/to_bloody = parent
+ to_bloody.add_blood_DNA(GET_ATOM_BLOOD_DNA(pool))
/**
* Find a blood decal on a turf that matches our last_blood_state
@@ -275,7 +284,8 @@
if(footprint_sprite)
src.footprint_sprite = footprint_sprite
if(!bloody_feet)
- bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER)
+ bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER, appearance_flags = RESET_COLOR)
+ bloody_feet.color = BLOOD_COLOR_RED
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
RegisterSignal(parent, COMSIG_STEP_ON_BLOOD, PROC_REF(on_step_blood))
@@ -305,7 +315,6 @@
FP.species_types |= affecting.limb_id
break
-
/datum/component/bloodysoles/feet/is_obscured()
if(wielder.shoes)
return TRUE
@@ -323,6 +332,14 @@
..()
+/datum/component/bloodysoles/feet/share_blood(obj/effect/decal/cleanable/pool)
+ . = ..()
+ if(.)
+ return
+
+ bloody_feet.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(pool))
+ update_icon()
+
/datum/component/bloodysoles/feet/proc/unequip_shoecover(datum/source)
SIGNAL_HANDLER
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 98ad9f17375..268a0cde078 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -73,7 +73,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
var/unique_enzymes
///Stores the hashed values of traits such as skin tones, hair style, and gender
var/unique_identity
- var/blood_type
+ /// The singleton blood type
+ var/datum/blood_type/blood_type
///The type of mutant race the player is if applicable (i.e. potato-man)
var/datum/species/species = new /datum/species/human
/// Assoc list of feature keys to their value
@@ -127,7 +128,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
return
destination.dna.unique_enzymes = unique_enzymes
destination.dna.unique_identity = unique_identity
- destination.dna.blood_type = blood_type
+ destination.set_blood_type(blood_type)
destination.dna.unique_features = unique_features
destination.dna.features = features.Copy()
destination.dna.real_name = real_name
@@ -465,7 +466,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
&& real_name == target_dna.real_name \
&& species.type == target_dna.species.type \
&& compare_list(features, target_dna.features) \
- && blood_type == target_dna.blood_type \
+ && blood_type.type == target_dna.blood_type.type \
)
return TRUE
@@ -510,7 +511,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
* * create_mutation_blocks - If true, generate_dna_blocks is called, which is used to set up mutation blocks (what a mob can naturally mutate).
* * randomize_features - If true, all entries in the features list will be randomized.
*/
-/datum/dna/proc/initialize_dna(newblood_type, create_mutation_blocks = TRUE, randomize_features = TRUE)
+/datum/dna/proc/initialize_dna(newblood_type = random_human_blood_type(), create_mutation_blocks = TRUE, randomize_features = TRUE)
if(newblood_type)
blood_type = newblood_type
if(create_mutation_blocks) //I hate this
@@ -659,7 +660,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
dna.generate_unique_enzymes()
if(newblood_type)
- dna.blood_type = newblood_type
+ set_blood_type(newblood_type)
if(unique_identity)
dna.unique_identity = unique_identity
diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm
index 9410558a60a..11f011a2e75 100644
--- a/code/datums/elements/decals/blood.dm
+++ b/code/datums/elements/decals/blood.dm
@@ -23,7 +23,7 @@
REMOVE_KEEP_TOGETHER(source_item, type)
return ..()
-/datum/element/decal/blood/generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color, _alpha, _smoothing, source)
+/datum/element/decal/blood/generate_appearance(_icon, _icon_state, _dir, _plane, _layer, _color = BLOOD_COLOR_RED, _alpha, _smoothing, source)
var/obj/item/as_item = source
ADD_KEEP_TOGETHER(as_item, type)
var/icon/icon_for_size = icon(as_item.icon, as_item.icon_state)
diff --git a/code/datums/materials/meat.dm b/code/datums/materials/meat.dm
index 134449b712a..175772d01a7 100644
--- a/code/datums/materials/meat.dm
+++ b/code/datums/materials/meat.dm
@@ -52,7 +52,7 @@
source.AddComponent(
/datum/component/bloody_spreader,\
blood_left = (protein_count + fat_count) * 0.3,\
- blood_dna = list("meaty DNA" = "MT-"),\
+ blood_dna = list("meaty DNA" = get_blood_type(BLOOD_TYPE_MEAT)),\
diseases = null,\
)
diff --git a/code/datums/quirks/negative_quirks/blood_deficiency.dm b/code/datums/quirks/negative_quirks/blood_deficiency.dm
index b0fa4cfeae4..d8a33ec3f36 100644
--- a/code/datums/quirks/negative_quirks/blood_deficiency.dm
+++ b/code/datums/quirks/negative_quirks/blood_deficiency.dm
@@ -13,10 +13,12 @@
/datum/quirk/blooddeficiency/add(client/client_source)
RegisterSignal(quirk_holder, COMSIG_HUMAN_ON_HANDLE_BLOOD, PROC_REF(lose_blood))
- RegisterSignal(quirk_holder, COMSIG_SPECIES_GAIN, PROC_REF(update_mail))
var/mob/living/carbon/human/human_holder = quirk_holder
+ if(!istype(human_holder))
+ return
update_mail(new_species = human_holder.dna.species)
+ RegisterSignal(quirk_holder, COMSIG_SPECIES_GAIN, PROC_REF(update_mail))
/datum/quirk/blooddeficiency/remove()
UnregisterSignal(quirk_holder, list(COMSIG_HUMAN_ON_HANDLE_BLOOD, COMSIG_SPECIES_GAIN))
@@ -44,17 +46,17 @@
/datum/quirk/blooddeficiency/proc/update_mail(datum/source, datum/species/new_species, datum/species/old_species, pref_load, regenerate_icons)
SIGNAL_HANDLER
- mail_goodies.Cut()
-
if(isnull(new_species.exotic_blood) && isnull(new_species.exotic_bloodtype))
- if(TRAIT_NOBLOOD in new_species.inherent_traits)
+ if(TRAIT_NOBLOOD in new_species.inherent_traits) // jellypeople have both exotic_blood and TRAIT_NOBLOOD
+ mail_goodies.Cut()
return
- mail_goodies += /obj/item/reagent_containers/blood/o_minus
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ var/datum/blood_type/blood_type = human_holder.dna.blood_type
+ if(isnull(blood_type))
return
for(var/obj/item/reagent_containers/blood/blood_bag as anything in typesof(/obj/item/reagent_containers/blood))
- var/right_blood_type = !isnull(new_species.exotic_bloodtype) && initial(blood_bag.blood_type) == new_species.exotic_bloodtype
- var/right_blood_reagent = !isnull(new_species.exotic_blood) && initial(blood_bag.unique_blood) == new_species.exotic_blood
- if(right_blood_type || right_blood_reagent)
- mail_goodies += blood_bag
+ if(blood_bag::blood_type == blood_type.name)
+ mail_goodies = list(blood_bag)
+ return
diff --git a/code/datums/quirks/neutral_quirks/evil.dm b/code/datums/quirks/neutral_quirks/evil.dm
index 95ecdb6bb8c..fc63937222f 100644
--- a/code/datums/quirks/neutral_quirks/evil.dm
+++ b/code/datums/quirks/neutral_quirks/evil.dm
@@ -15,3 +15,31 @@
var/evil_policy = get_policy("[type]") || "Please note that while you may be [LOWER_TEXT(name)], this does NOT give you any additional right to attack people or cause chaos."
// We shouldn't need this, but it prevents people using it as a dumb excuse in ahelps.
to_chat(quirk_holder, span_big(span_info(evil_policy)))
+ RegisterSignal(quirk_holder, COMSIG_SPECIES_GAIN, PROC_REF(make_blood_evil))
+
+/datum/quirk/evil/add(client/client_source)
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ if(!istype(human_holder))
+ return
+ make_blood_evil(new_species = human_holder.dna.species)
+
+/datum/quirk/evil/remove()
+ UnregisterSignal(quirk_holder, list(COMSIG_SPECIES_GAIN))
+
+/// Get a dynamically generated blood type based off the mob's old blood type and make it 'evil'. Needs to happen whenever the species changes.
+/datum/quirk/evil/proc/make_blood_evil(datum/source, datum/species/new_species, datum/species/old_species, pref_load, regenerate_icons)
+ SIGNAL_HANDLER
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+ if(!istype(human_holder))
+ return
+
+ // Try to find a corresponding evil blood type for this
+ var/datum/blood_type/new_blood_type = get_blood_type("[human_holder.dna.blood_type.id]_but_evil")
+ if(isnull(new_blood_type)) // this blood type doesn't exist yet in the global list, so make a new one
+ new_blood_type = new /datum/blood_type/evil(human_holder.dna.blood_type, human_holder.dna.blood_type.compatible_types)
+ GLOB.blood_types[new_blood_type.id] = new_blood_type
+ human_holder.set_blood_type(new_blood_type)
+
+ if(new_species.exotic_bloodtype)
+ new_species.exotic_bloodtype = new_blood_type
diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm
index 0a0b8eb0785..f66de1bb759 100644
--- a/code/datums/records/manifest.dm
+++ b/code/datums/records/manifest.dm
@@ -124,7 +124,7 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
var/datum/record/locked/lockfile = new(
age = person.age,
chrono_age = person.chrono_age, // SKYRAT EDIT ADDITION - Chronological age
- blood_type = record_dna.blood_type,
+ blood_type = record_dna.blood_type.name,
character_appearance = character_appearance,
dna_string = record_dna.unique_enzymes,
fingerprint = md5(record_dna.unique_identity),
@@ -146,7 +146,7 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
new /datum/record/crew(
age = person.age,
chrono_age = person.chrono_age, // SKYRAT EDIT ADDITION - Chronological age
- blood_type = record_dna.blood_type,
+ blood_type = record_dna.blood_type.name,
character_appearance = character_appearance,
dna_string = record_dna.unique_enzymes,
fingerprint = md5(record_dna.unique_identity),
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index c567f8214fa..f278b29f6b8 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -490,44 +490,23 @@
/mob/living/proc/get_blood_dna_list()
if(get_blood_id() != /datum/reagent/blood)
return
- return list("ANIMAL DNA" = "Y-")
+ return list("ANIMAL DNA" = get_blood_type(BLOOD_TYPE_ANIMAL))
///Get the mobs dna list
/mob/living/carbon/get_blood_dna_list()
- if(get_blood_id() != /datum/reagent/blood)
- return
var/list/blood_dna = list()
if(dna)
blood_dna[dna.unique_enzymes] = dna.blood_type
else
- blood_dna["UNKNOWN DNA"] = "X*"
+ blood_dna["UNKNOWN DNA"] = get_blood_type(BLOOD_TYPE_XENO)
return blood_dna
/mob/living/carbon/alien/get_blood_dna_list()
- return list("UNKNOWN DNA" = "X*")
+ return list("UNKNOWN DNA" = get_blood_type(BLOOD_TYPE_XENO))
/mob/living/silicon/get_blood_dna_list()
return
-///to add a mob's dna info into an object's blood_dna list.
-/atom/proc/transfer_mob_blood_dna(mob/living/injected_mob)
- // Returns 0 if we have that blood already
- var/new_blood_dna = injected_mob.get_blood_dna_list()
- if(!new_blood_dna)
- return FALSE
- var/old_length = GET_ATOM_BLOOD_DNA_LENGTH(src)
- add_blood_DNA(new_blood_dna)
- if(GET_ATOM_BLOOD_DNA_LENGTH(src) == 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/injected_mob)
- var/list/blood_dna = injected_mob.get_blood_dna_list()
- if(!blood_dna)
- return FALSE
- return add_blood_DNA(blood_dna)
-
///Is this atom in space
/atom/proc/isinspace()
if(isspaceturf(get_turf(src)))
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index d224e31d3d3..a974499a8ec 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -1755,7 +1755,7 @@
scanner_occupant.real_name = buffer_slot["name"]
scanner_occupant.name = buffer_slot["name"]
scanner_occupant.dna.unique_enzymes = buffer_slot["UE"]
- scanner_occupant.dna.blood_type = buffer_slot["blood_type"]
+ scanner_occupant.set_blood_type(buffer_slot["blood_type"])
scanner_occupant.apply_status_effect(/datum/status_effect/genetic_damage, damage_increase)
scanner_occupant.domutcheck()
return TRUE
@@ -1773,7 +1773,7 @@
scanner_occupant.real_name = buffer_slot["name"]
scanner_occupant.name = buffer_slot["name"]
scanner_occupant.dna.unique_enzymes = buffer_slot["UE"]
- scanner_occupant.dna.blood_type = buffer_slot["blood_type"]
+ scanner_occupant.set_blood_type(buffer_slot["blood_type"])
scanner_occupant.apply_status_effect(/datum/status_effect/genetic_damage, damage_increase)
scanner_occupant.domutcheck()
return TRUE
diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm
index 57630c8f16b..e1ed92bb8d8 100644
--- a/code/game/machinery/computer/operating_computer.dm
+++ b/code/game/machinery/computer/operating_computer.dm
@@ -123,15 +123,7 @@
data["patient"]["stat"] = "Dead"
data["patient"]["statstate"] = "bad"
data["patient"]["health"] = patient.health
-
- // check here to see if the patient has standard blood reagent, or special blood (like how ethereals bleed liquid electricity) to show the proper name in the computer
- var/blood_id = patient.get_blood_id()
- if(blood_id == /datum/reagent/blood)
- data["patient"]["blood_type"] = patient.dna?.blood_type
- else
- var/datum/reagent/special_blood = GLOB.chemical_reagents_list[blood_id]
- data["patient"]["blood_type"] = special_blood ? special_blood.name : blood_id
-
+ data["patient"]["blood_type"] = patient.dna?.blood_type.name
data["patient"]["maxHealth"] = patient.maxHealth
data["patient"]["minHealth"] = HEALTH_THRESHOLD_DEAD
data["patient"]["bruteLoss"] = patient.getBruteLoss()
diff --git a/code/game/machinery/computer/records/medical.dm b/code/game/machinery/computer/records/medical.dm
index 58f3fe65fc8..c564c71f310 100644
--- a/code/game/machinery/computer/records/medical.dm
+++ b/code/game/machinery/computer/records/medical.dm
@@ -150,7 +150,7 @@
target.age = 18
target.chrono_age = 18 // SKYRAT EDIT ADDITION - Chronological age
- target.blood_type = pick(list("A+", "A-", "B+", "B-", "O+", "O-", "AB+", "AB-"))
+ target.blood_type = pick(list(BLOOD_TYPE_A_PLUS, BLOOD_TYPE_A_MINUS, BLOOD_TYPE_B_PLUS, BLOOD_TYPE_B_MINUS, BLOOD_TYPE_O_PLUS, BLOOD_TYPE_O_MINUS, BLOOD_TYPE_AB_PLUS, BLOOD_TYPE_AB_MINUS))
target.dna_string = "Unknown"
target.gender = "Unknown"
target.major_disabilities = ""
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index c669c46e4c0..73426e3bdfb 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -185,6 +185,10 @@
. = ..()
icon_state = "datadisk[rand(0,7)]"
add_overlay("datadisk_gene")
+ if(length(genetic_makeup_buffer))
+ var/datum/blood_type = genetic_makeup_buffer["blood_type"]
+ if(blood_type)
+ blood_type = get_blood_type(blood_type) || random_human_blood_type()
/obj/item/disk/data/debug
name = "\improper CentCom DNA disk"
diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm
index 292df504a64..83e90bc98c5 100644
--- a/code/game/machinery/medical_kiosk.dm
+++ b/code/game/machinery/medical_kiosk.dm
@@ -237,7 +237,7 @@
var/bleed_status = "Patient is not currently bleeding."
var/blood_status = " Patient either has no blood, or does not require it to function."
var/blood_percent = round((patient.blood_volume / BLOOD_VOLUME_NORMAL)*100)
- var/blood_type = patient.dna.blood_type
+ var/datum/blood_type/blood_type = patient.dna.blood_type
var/blood_warning = " "
var/blood_alcohol = patient.get_blood_alcohol_content()
@@ -254,7 +254,7 @@
blood_warning = " Patient has low blood levels. Seek a large meal, or iron supplements."
if(blood_percent <= 60)
blood_warning = " Patient has DANGEROUSLY low blood levels. Seek a blood transfusion, iron supplements, or saline glucose immedietly. Ignoring treatment may lead to death!"
- blood_status = "Patient blood levels are currently reading [blood_percent]%. Patient has [ blood_type] type blood. [blood_warning]"
+ blood_status = "Patient blood levels are currently reading [blood_percent]%. Patient has [ blood_type.name] type blood. [blood_warning]"
var/trauma_status = "Patient is free of unique brain trauma."
var/brain_loss = patient.get_organ_loss(ORGAN_SLOT_BRAIN)
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 551ea5d519b..2b32e30ac3d 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -100,9 +100,22 @@
var/is_powered = !(machine_stat & (BROKEN|NOPOWER))
if(safety_mode)
is_powered = FALSE
- icon_state = icon_name + "[is_powered]" + "[(bloody ? "bld" : "")]" // add the blood tag at the end
+ icon_state = icon_name + "[is_powered]"
return ..()
+/obj/machinery/recycler/update_overlays()
+ . = ..()
+ if(!bloody)
+ return
+
+ var/mutable_appearance/blood_overlay = mutable_appearance(icon, "[icon_state]bld", appearance_flags = RESET_COLOR|KEEP_APART)
+ var/blood_dna = GET_ATOM_BLOOD_DNA(src)
+ if(blood_dna)
+ blood_overlay.color = get_blood_dna_color(blood_dna)
+ else
+ blood_overlay.color = BLOOD_COLOR_RED
+ . += blood_overlay
+
/obj/machinery/recycler/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
if(!anchored)
@@ -233,26 +246,26 @@
safety_mode = FALSE
update_appearance()
-/obj/machinery/recycler/proc/crush_living(mob/living/L)
- L.forceMove(loc)
+/obj/machinery/recycler/proc/crush_living(mob/living/living_mob)
+ living_mob.forceMove(loc)
- if(issilicon(L))
+ if(issilicon(living_mob))
playsound(src, 'sound/items/tools/welder.ogg', 50, TRUE)
else
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
- if(iscarbon(L))
- if(L.stat == CONSCIOUS)
- L.say("ARRRRRRRRRRRGH!!!", forced="recycler grinding")
- add_mob_blood(L)
+ if(iscarbon(living_mob))
+ if(living_mob.stat == CONSCIOUS)
+ living_mob.say("ARRRRRRRRRRRGH!!!", forced= "recycler grinding")
+ add_mob_blood(living_mob)
- if(!bloody && !issilicon(L))
+ if(!bloody && !issilicon(living_mob))
bloody = TRUE
- update_appearance()
// Instantly lie down, also go unconscious from the pain, before you die.
- L.Unconscious(100)
- L.adjustBruteLoss(crush_damage)
+ living_mob.Unconscious(100)
+ living_mob.adjustBruteLoss(crush_damage)
+ update_appearance()
/obj/machinery/recycler/on_deconstruction(disassembled)
safety_mode = TRUE
diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm
index 15b8ca546af..66d01cd2f7d 100644
--- a/code/game/objects/effects/decals/cleanable.dm
+++ b/code/game/objects/effects/decals/cleanable.dm
@@ -150,6 +150,9 @@
return null
+/obj/effect/decal/cleanable/blood/get_blood_color()
+ return color || ..()
+
/obj/effect/decal/cleanable/proc/handle_merge_decal(obj/effect/decal/cleanable/merger)
if(!merger)
return
diff --git a/code/game/objects/effects/decals/cleanable/aliens.dm b/code/game/objects/effects/decals/cleanable/aliens.dm
index f2543f33c86..74c38afe755 100644
--- a/code/game/objects/effects/decals/cleanable/aliens.dm
+++ b/code/game/objects/effects/decals/cleanable/aliens.dm
@@ -4,16 +4,21 @@
name = "xeno blood"
desc = "It's green and acidic. It looks like... blood?"
icon = 'icons/effects/blood.dmi'
- icon_state = "xfloor1"
- random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7")
+ icon_state = "floor1"
+ random_icon_states = list("floor1", "floor2", "floor3", "floor4", "floor5", "floor6", "floor7")
bloodiness = BLOOD_AMOUNT_PER_DECAL
blood_state = BLOOD_STATE_XENO
beauty = -250
clean_type = CLEAN_TYPE_BLOOD
+/obj/effect/decal/cleanable/xenoblood/add_blood_DNA(list/blood_DNA, no_visuals = FALSE)
+ . = ..()
+ if(!no_visuals && length(blood_DNA))
+ color = get_blood_dna_color(blood_DNA)
+
/obj/effect/decal/cleanable/xenoblood/Initialize(mapload)
. = ..()
- add_blood_DNA(list("UNKNOWN DNA" = "X*"))
+ add_blood_DNA(list("UNKNOWN DNA" = get_blood_type(BLOOD_TYPE_XENO)))
/obj/effect/decal/cleanable/xenoblood/xsplatter
random_icon_states = list("xgibbl1", "xgibbl2", "xgibbl3", "xgibbl4", "xgibbl5")
@@ -33,6 +38,13 @@
/obj/effect/decal/cleanable/xenoblood/xgibs/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_MOVABLE_PIPE_EJECTING, PROC_REF(on_pipe_eject))
+ update_appearance(UPDATE_OVERLAYS)
+
+/obj/effect/decal/cleanable/xenoblood/xgibs/update_overlays()
+ . = ..()
+ var/mutable_appearance/gib_overlay = mutable_appearance(icon, "[icon_state]-overlay", appearance_flags = KEEP_APART|RESET_COLOR)
+ if(gib_overlay)
+ . += gib_overlay
/obj/effect/decal/cleanable/xenoblood/xgibs/proc/streak(list/directions, mapload=FALSE)
SEND_SIGNAL(src, COMSIG_GIBS_STREAK, directions)
@@ -45,7 +57,8 @@
for (var/i in 1 to range)
var/turf/my_turf = get_turf(src)
if(!isgroundlessturf(my_turf) || GET_TURF_BELOW(my_turf))
- new /obj/effect/decal/cleanable/xenoblood/xsplatter(my_turf)
+ var/obj/effect/decal/cleanable/xenoblood/xsplatter/new_splatter = new /obj/effect/decal/cleanable/xenoblood/xsplatter(my_turf)
+ new_splatter.add_blood_DNA(GET_ATOM_BLOOD_DNA(src))
if (!step_to(src, get_step(src, direction), 0))
break
return
@@ -106,9 +119,9 @@
random_icon_states = list("xgiblarvahead", "xgiblarvatorso")
/obj/effect/decal/cleanable/blood/xtracks
- icon_state = "xtracks"
+ icon_state = "tracks"
random_icon_states = null
/obj/effect/decal/cleanable/blood/xtracks/Initialize(mapload)
. = ..()
- add_blood_DNA(list("Unknown DNA" = "X*"))
+ add_blood_DNA(list("Unknown DNA" = get_blood_type(BLOOD_TYPE_XENO)))
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index b146dec132e..45084d3ebf3 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -8,11 +8,14 @@
bloodiness = BLOOD_AMOUNT_PER_DECAL
beauty = -100
clean_type = CLEAN_TYPE_BLOOD
+ color = BLOOD_COLOR_RED
var/should_dry = TRUE
var/dryname = "dried blood" //when the blood lasts long enough, it becomes dry and gets a new name
var/drydesc = "Looks like it's been here a while. Eew." //as above
var/drytime = 0
var/footprint_sprite = null
+ /// If we've dried already
+ var/has_dried
/obj/effect/decal/cleanable/blood/Initialize(mapload)
. = ..()
@@ -21,7 +24,7 @@
if(bloodiness)
start_drying()
else
- dry()
+ dry(freshly_made = TRUE)
/obj/effect/decal/cleanable/blood/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -31,6 +34,11 @@
if(world.time > drytime)
dry()
+/obj/effect/decal/cleanable/blood/add_blood_DNA(list/blood_DNA, no_visuals = FALSE)
+ . = ..()
+ if(!no_visuals && length(blood_DNA))
+ color = get_blood_dna_color(blood_DNA)
+
/obj/effect/decal/cleanable/blood/proc/get_timer()
drytime = world.time + 3 MINUTES
@@ -39,19 +47,36 @@
START_PROCESSING(SSobj, src)
///This is what actually "dries" the blood. Returns true if it's all out of blood to dry, and false otherwise
-/obj/effect/decal/cleanable/blood/proc/dry()
+/obj/effect/decal/cleanable/blood/proc/dry(freshly_made = FALSE)
+ if(freshly_made)
+ start_drying()
+ return FALSE
if(bloodiness > 20)
bloodiness -= BLOOD_AMOUNT_PER_DECAL
get_timer()
return FALSE
- else
- name = dryname
- desc = drydesc
- bloodiness = 0
- color = COLOR_GRAY //not all blood splatters have their own sprites... It still looks pretty nice
- STOP_PROCESSING(SSobj, src)
+
+ name = dryname
+ has_dried = TRUE
+ desc = drydesc
+ bloodiness = 0
+ STOP_PROCESSING(SSobj, src)
+ // We're not using a matrix so we're free to use BlendRGB
+ if(!islist(color))
+ add_atom_colour(BlendRGB(color, COLOR_BLACK, 0.5), FIXED_COLOUR_PRIORITY)
return TRUE
+ // We're using a matrix, so we need to halve all values
+ var/list/blood_matrix = color
+ for(var/i in 1 to min(length(blood_matrix), 16))
+ if (length(blood_matrix) == 12 && i > 9) // Don't modify constants
+ break
+ if (length(blood_matrix) >= 16 && i % 4 == 0) // Don't modify alpha either
+ continue
+ blood_matrix[i] *= 0.5
+ color = blood_matrix
+ return TRUE
+
/obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C)
C.add_blood_DNA(GET_ATOM_BLOOD_DNA(src))
if (bloodiness)
@@ -63,7 +88,7 @@
icon_state = "floor1-old"
/obj/effect/decal/cleanable/blood/old/Initialize(mapload, list/datum/disease/diseases)
- add_blood_DNA(list("Non-human DNA" = random_blood_type())) // Needs to happen before ..()
+ add_blood_DNA(list("Non-human DNA" = random_human_blood_type())) // Needs to happen before ..()
return ..()
/obj/effect/decal/cleanable/blood/splatter
@@ -132,17 +157,24 @@
. = ..()
AddElement(/datum/element/squish_sound)
RegisterSignal(src, COMSIG_MOVABLE_PIPE_EJECTING, PROC_REF(on_pipe_eject))
+ update_appearance(UPDATE_OVERLAYS)
-/obj/effect/decal/cleanable/blood/gibs/Destroy()
- return ..()
+/obj/effect/decal/cleanable/blood/gibs/update_overlays()
+ . = ..()
+ var/mutable_appearance/gib_overlay = mutable_appearance(icon, "[icon_state]-overlay", appearance_flags = KEEP_APART|RESET_COLOR)
+ if(gib_overlay)
+ if(has_dried)
+ gib_overlay.color = COLOR_GRAY
+ . += gib_overlay
/obj/effect/decal/cleanable/blood/gibs/replace_decal(obj/effect/decal/cleanable/C)
return FALSE //Never fail to place us
-/obj/effect/decal/cleanable/blood/gibs/dry()
+/obj/effect/decal/cleanable/blood/gibs/dry(freshly_made = FALSE)
. = ..()
if(!.)
return
+ update_appearance(UPDATE_OVERLAYS)
//AddComponent(/datum/component/rot, 0, 5 MINUTES, 0.7) skyrat edit
/obj/effect/decal/cleanable/blood/gibs/ex_act(severity, target)
@@ -166,23 +198,27 @@
var/range = pick(0, 200; 1, 150; 2, 50; 3, 17; 50) //the 3% chance of 50 steps is intentional and played for laughs.
if(!step_to(src, get_step(src, direction), 0))
return
- if(mapload)
- for (var/i in 1 to range)
- var/turf/my_turf = get_turf(src)
- if(!isgroundlessturf(my_turf) || GET_TURF_BELOW(my_turf))
- new /obj/effect/decal/cleanable/blood/splatter(my_turf)
- if (!step_to(src, get_step(src, direction), 0))
- break
+
+ if(!mapload)
+ var/datum/move_loop/loop = GLOB.move_manager.move_to(src, get_step(src, direction), delay = delay, timeout = range * delay, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(spread_movement_effects))
return
- var/datum/move_loop/loop = GLOB.move_manager.move_to(src, get_step(src, direction), delay = delay, timeout = range * delay, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
- RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(spread_movement_effects))
+ for (var/i in 1 to range)
+ var/turf/my_turf = get_turf(src)
+ if(!isgroundlessturf(my_turf) || GET_TURF_BELOW(my_turf))
+ var/obj/effect/decal/cleanable/blood/splatter/new_splatter = new /obj/effect/decal/cleanable/blood/splatter(my_turf)
+ new_splatter.add_blood_DNA(GET_ATOM_BLOOD_DNA(src))
+
+ if (!step_to(src, get_step(src, direction), 0))
+ break
/obj/effect/decal/cleanable/blood/gibs/proc/spread_movement_effects(datum/move_loop/has_target/source)
SIGNAL_HANDLER
if(NeverShouldHaveComeHere(loc))
return
- new /obj/effect/decal/cleanable/blood/splatter(loc)
+ var/obj/effect/decal/cleanable/blood/splatter/new_splatter = new /obj/effect/decal/cleanable/blood/splatter(loc)
+ new_splatter.add_blood_DNA(GET_ATOM_BLOOD_DNA(src))
/obj/effect/decal/cleanable/blood/gibs/up
icon_state = "gibup1"
@@ -220,7 +256,7 @@
/obj/effect/decal/cleanable/blood/gibs/old/Initialize(mapload, list/datum/disease/diseases)
. = ..()
setDir(pick(1,2,4,8))
- add_blood_DNA(list("Non-human DNA" = random_blood_type()))
+ add_blood_DNA(list("Non-human DNA" = random_human_blood_type()))
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLUDGE, CELL_VIRUS_TABLE_GENERIC, rand(2,4), 10)
dry()
@@ -237,7 +273,6 @@
/obj/effect/decal/cleanable/blood/drip/can_bloodcrawl_in()
return TRUE
-
//BLOODY FOOTPRINTS
/obj/effect/decal/cleanable/blood/footprints
name = "footprints"
@@ -318,33 +353,39 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
var/image/bloodstep_overlay = GLOB.bloody_footprints_cache["entered-[footprint_sprite]-[blood_state]-[Ddir]"]
if(!bloodstep_overlay)
GLOB.bloody_footprints_cache["entered-[footprint_sprite]-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]_[footprint_sprite]_enter", dir = Ddir)
+ bloodstep_overlay.color = color
. += bloodstep_overlay
if(exited_dirs & Ddir)
var/image/bloodstep_overlay = GLOB.bloody_footprints_cache["exited-[footprint_sprite]-[blood_state]-[Ddir]"]
if(!bloodstep_overlay)
GLOB.bloody_footprints_cache["exited-[footprint_sprite]-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]_[footprint_sprite]_exit", dir = Ddir)
+ bloodstep_overlay.color = color
. += bloodstep_overlay
/obj/effect/decal/cleanable/blood/footprints/examine(mob/user)
. = ..()
- if((shoe_types.len + species_types.len) > 0)
- . += "You recognise \the [src] as belonging to:"
- for(var/sole in shoe_types)
- var/obj/item/clothing/item = sole
- var/article = initial(item.gender) == PLURAL ? "Some" : "A"
- . += "[icon2html(initial(item.icon), user, initial(item.icon_state))] [article] [initial(item.name)]."
- for(var/species in species_types)
- // god help me
- if(species == "unknown")
- . += "Some feet."
- else if(species == SPECIES_MONKEY)
- . += "[icon2html('icons/mob/human/human.dmi', user, "monkey")] Some monkey paws."
- else if(species == SPECIES_HUMAN)
- . += "[icon2html('icons/mob/human/bodyparts.dmi', user, "default_human_l_leg")] Some human feet."
- else
- . += "[icon2html('icons/mob/human/bodyparts.dmi', user, "[species]_l_leg")] Some [species] feet."
+ if(length(shoe_types) + length(species_types) == 0)
+ return
+
+ . += "You recognise \the [src] as belonging to:"
+
+ for(var/sole in shoe_types)
+ var/obj/item/clothing/item = sole
+ var/article = initial(item.gender) == PLURAL ? "Some" : "A"
+ . += "[icon2html(initial(item.icon), user, initial(item.icon_state))] [article] [initial(item.name)]."
+
+ for(var/species in species_types)
+ // god help me
+ if(species == "unknown")
+ . += "Some feet."
+ else if(species == SPECIES_MONKEY)
+ . += "[icon2html('icons/mob/human/human.dmi', user, "monkey")] Some monkey paws."
+ else if(species == SPECIES_HUMAN)
+ . += "[icon2html('icons/mob/human/bodyparts.dmi', user, "default_human_l_leg")] Some human feet."
+ else
+ . += "[icon2html('icons/mob/human/bodyparts.dmi', user, "[species]_l_leg")] Some [species] feet."
/obj/effect/decal/cleanable/blood/footprints/replace_decal(obj/effect/decal/cleanable/blood/blood_decal)
if(blood_state != blood_decal.blood_state || footprint_sprite != blood_decal.footprint_sprite) //We only replace footprints of the same type as us
@@ -381,12 +422,12 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
if(splatter_strength)
src.splatter_strength = splatter_strength
-/obj/effect/decal/cleanable/blood/hitsplatter/Destroy()
+/obj/effect/decal/cleanable/blood/hitsplatter/proc/expire()
if(isturf(loc) && !skip)
playsound(src, 'sound/effects/wounds/splatter.ogg', 60, TRUE, -1)
if(blood_dna_info)
loc.add_blood_DNA(blood_dna_info)
- return ..()
+ qdel(src)
/// Set the splatter up to fly through the air until it rounds out of steam or hits something
/obj/effect/decal/cleanable/blood/hitsplatter/proc/fly_towards(turf/target_turf, range)
@@ -402,65 +443,63 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
/obj/effect/decal/cleanable/blood/hitsplatter/proc/post_move(datum/move_loop/source)
SIGNAL_HANDLER
- for(var/atom/iter_atom in get_turf(src))
+
+ for(var/atom/movable/iter_atom in loc)
if(hit_endpoint)
return
+ if(iter_atom == src || iter_atom.invisibility || iter_atom.alpha <= 0 || (isobj(iter_atom) && !iter_atom.density))
+ continue
if(splatter_strength <= 0)
break
- if(isitem(iter_atom))
- iter_atom.add_blood_DNA(blood_dna_info)
- splatter_strength--
- else if(ishuman(iter_atom))
- var/mob/living/carbon/human/splashed_human = iter_atom
- if(splashed_human.wear_suit)
- splashed_human.wear_suit.add_blood_DNA(blood_dna_info)
- splashed_human.update_worn_oversuit() //updates mob overlays to show the new blood (no refresh)
- if(splashed_human.w_uniform)
- splashed_human.w_uniform.add_blood_DNA(blood_dna_info)
- splashed_human.update_worn_undersuit() //updates mob overlays to show the new blood (no refresh)
- splatter_strength--
+ iter_atom.add_blood_DNA(blood_dna_info)
+ splatter_strength--
+
if(splatter_strength <= 0) // we used all the puff so we delete it.
- qdel(src)
+ expire()
/obj/effect/decal/cleanable/blood/hitsplatter/proc/loop_done(datum/source)
SIGNAL_HANDLER
if(!QDELETED(src))
- qdel(src)
+ expire()
/obj/effect/decal/cleanable/blood/hitsplatter/Bump(atom/bumped_atom)
if(!iswallturf(bumped_atom) && !istype(bumped_atom, /obj/structure/window))
- qdel(src)
+ expire()
return
if(istype(bumped_atom, /obj/structure/window))
var/obj/structure/window/bumped_window = bumped_atom
if(!bumped_window.fulltile)
hit_endpoint = TRUE
- qdel(src)
+ expire()
return
hit_endpoint = TRUE
- if(isturf(prev_loc))
+ if(!isturf(prev_loc)) // This will only happen if prev_loc is not even a turf, which is highly unlikely.
abstract_move(bumped_atom)
- skip = TRUE
- //Adjust pixel offset to make splatters appear on the wall
- if(istype(bumped_atom, /obj/structure/window))
- land_on_window(bumped_atom)
- else
- var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new(prev_loc)
- final_splatter.pixel_x = (dir == EAST ? 32 : (dir == WEST ? -32 : 0))
- final_splatter.pixel_y = (dir == NORTH ? 32 : (dir == SOUTH ? -32 : 0))
- else // This will only happen if prev_loc is not even a turf, which is highly unlikely.
- abstract_move(bumped_atom)
- qdel(src)
+ expire()
+ return
+
+ abstract_move(bumped_atom)
+ skip = TRUE
+ //Adjust pixel offset to make splatters appear on the wall
+ if(istype(bumped_atom, /obj/structure/window))
+ land_on_window(bumped_atom)
+ return
+
+ var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new(prev_loc)
+ final_splatter.add_blood_DNA(blood_dna_info)
+ final_splatter.pixel_x = (dir == EAST ? 32 : (dir == WEST ? -32 : 0))
+ final_splatter.pixel_y = (dir == NORTH ? 32 : (dir == SOUTH ? -32 : 0))
/// A special case for hitsplatters hitting windows, since those can actually be moved around, store it in the window and slap it in the vis_contents
/obj/effect/decal/cleanable/blood/hitsplatter/proc/land_on_window(obj/structure/window/the_window)
if(!the_window.fulltile)
return
- var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new
+ var/obj/effect/decal/cleanable/final_splatter = new /obj/effect/decal/cleanable/blood/splatter/over_window(prev_loc)
+ final_splatter.add_blood_DNA(blood_dna_info)
final_splatter.forceMove(the_window)
the_window.vis_contents += final_splatter
the_window.bloodied = TRUE
- qdel(src)
+ expire()
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index dc46585dd40..4921c4fa4ed 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -313,8 +313,9 @@
name = "insect guts"
desc = "One bug squashed. Four more will rise in its place."
icon = 'icons/effects/blood.dmi'
- icon_state = "xfloor1"
- random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7")
+ icon_state = "floor1"
+ random_icon_states = list("floor1", "floor2", "floor3", "floor4", "floor5", "floor6", "floor7")
+ color = BLOOD_COLOR_XENO
/obj/effect/decal/cleanable/confetti
name = "confetti"
diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm
index 88d16c87acd..66006a8b3ec 100644
--- a/code/game/objects/effects/spawners/gibspawner.dm
+++ b/code/game/objects/effects/spawners/gibspawner.dm
@@ -9,8 +9,9 @@
var/list/gibtypes = list() //typepaths of the gib decals to spawn
var/list/gibamounts = list() //amount to spawn for each gib decal type we'll spawn.
var/list/gibdirections = list() //of lists of possible directions to spread each gib decal type towards.
+ var/blood_dna_info // Cached blood_dna_info in case we do not have a source mob
-/obj/effect/gibspawner/Initialize(mapload, mob/living/source_mob, list/datum/disease/diseases)
+/obj/effect/gibspawner/Initialize(mapload, mob/living/source_mob, list/datum/disease/diseases, blood_dna_info)
. = ..()
if(gibtypes.len != gibamounts.len)
@@ -32,14 +33,16 @@
var/list/dna_to_add //find the dna to pass to the spawned gibs. do note this can be null if the mob doesn't have blood. add_blood_DNA() has built in null handling.
- if(source_mob)
+ if(blood_dna_info)
+ dna_to_add = blood_dna_info
+ else if(source_mob)
dna_to_add = source_mob.get_blood_dna_list() //ez pz
else if(gib_mob_type)
var/mob/living/temp_mob = new gib_mob_type(src) //generate a fake mob so that we pull the right type of DNA for the gibs.
dna_to_add = temp_mob.get_blood_dna_list()
qdel(temp_mob)
else
- dna_to_add = list("Non-human DNA" = random_blood_type()) //else, generate a random bloodtype for it.
+ dna_to_add = list("Non-human DNA" = random_human_blood_type()) //else, generate a random bloodtype for it.
for(var/i in 1 to gibtypes.len)
@@ -66,7 +69,7 @@
gibamounts = list(2, 2, 1)
sound_vol = 40
-/obj/effect/gibspawner/generic/Initialize(mapload)
+/obj/effect/gibspawner/generic/Initialize(mapload, blood_dna_info)
if(!gibdirections.len)
gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list())
return ..()
@@ -80,7 +83,7 @@
gib_mob_type = /mob/living/carbon/human
sound_vol = 50
-/obj/effect/gibspawner/human/Initialize(mapload)
+/obj/effect/gibspawner/human/Initialize(mapload, mob/living/source_mob, list/datum/disease/diseases, blood_dna_info)
if(!gibdirections.len)
gibdirections = list(
list(NORTH, NORTHEAST, NORTHWEST),
@@ -91,6 +94,9 @@
GLOB.alldirs,
list(),
)
+ if(!iscarbon(source_mob) && isnull(blood_dna_info))
+ return ..(blood_dna_info = list("Human DNA" = random_human_blood_type()))
+
return ..()
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 23687d205ff..9b9bb2b85ff 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -7,7 +7,14 @@
plane = GAME_PLANE
var/splatter_type = "splatter"
-/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir)
+// set_color arg can be either a color string or a singleton /datum/blood_type to pull the color from
+/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir, set_color = BLOOD_COLOR_RED)
+ if(set_color)
+ var/datum/blood_type/blood_type = set_color
+ if(istype(blood_type))
+ color = blood_type.color
+ else
+ color = set_color
if(ISDIAGONALDIR(set_dir))
icon_state = "[splatter_type][pick(1, 2, 6)]"
else
@@ -44,6 +51,9 @@
/obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter
splatter_type = "xsplatter"
+/obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter/Initialize(mapload, set_dir, set_color = GLOB.blood_types[/datum/blood_type/xeno::name])
+ return ..()
+
/obj/effect/temp_visual/dir_setting/speedbike_trail
name = "speedbike trails"
icon_state = "ion_fade"
diff --git a/code/game/objects/items/devices/scanners/autopsy_scanner.dm b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
index a054b3c69d2..7b9fdd81c7c 100644
--- a/code/game/objects/items/devices/scanners/autopsy_scanner.dm
+++ b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
@@ -92,11 +92,8 @@
var/blood_id = scanned.get_blood_id()
if(blood_id)
var/blood_percent = round((scanned.blood_volume / BLOOD_VOLUME_NORMAL) * 100)
- var/blood_type = scanned.dna.blood_type
- if(blood_id != /datum/reagent/blood)
- var/datum/reagent/reagents = GLOB.chemical_reagents_list[blood_id]
- blood_type = reagents?.name || blood_id
- autopsy_information += "Blood Type: [blood_type]
"
+ var/datum/blood_type/blood_type = scanned.dna.blood_type
+ autopsy_information += "Blood Type: [blood_type.name]
"
autopsy_information += "Blood Volume: [scanned.blood_volume] cl ([blood_percent]%)
"
for(var/datum/disease/diseases as anything in scanned.diseases)
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 59b6fc8b934..cdc76f9d86e 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -377,14 +377,13 @@
var/blood_id = carbontarget.get_blood_id()
if(blood_id)
var/blood_percent = round((carbontarget.blood_volume / BLOOD_VOLUME_NORMAL) * 100)
- var/blood_type = carbontarget.dna.blood_type
- if(blood_id != /datum/reagent/blood) // special blood substance
- var/datum/reagent/real_reagent = GLOB.chemical_reagents_list[blood_id]
- blood_type = real_reagent?.name || blood_id
+ var/datum/blood_type/blood_type = carbontarget.dna.blood_type
if(carbontarget.blood_volume <= BLOOD_VOLUME_SAFE && carbontarget.blood_volume > BLOOD_VOLUME_OKAY)
- render_list += "Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]
"
+ render_list += "Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type.name]")]
"
+ render_list += "Recommendation: [blood_type.restoration_chem::name] supplements or blood transfusion.
"
else if(carbontarget.blood_volume <= BLOOD_VOLUME_OKAY)
render_list += "Blood level: CRITICAL [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]
"
+ render_list += "Recommendation: [blood_type.restoration_chem::name] supplements or blood transfusion.
"
else
render_list += "Blood level: [blood_percent]%, [carbontarget.blood_volume] cl, type: [blood_type]
"
diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm
index 2bd19f5e619..5d298ff1db4 100644
--- a/code/game/objects/items/dna_injector.dm
+++ b/code/game/objects/items/dna_injector.dm
@@ -57,7 +57,7 @@
target.real_name = fields["name"]
target.dna.unique_enzymes = fields["UE"]
target.name = target.real_name
- target.dna.blood_type = fields["blood_type"]
+ target.set_blood_type(fields["blood_type"])
if(fields["UI"]) //UI+UE
target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"])
if(fields["UF"])
@@ -135,7 +135,7 @@
target.real_name = fields["name"]
target.dna.unique_enzymes = fields["UE"]
target.name = target.real_name
- target.dna.blood_type = fields["blood_type"]
+ target.set_blood_type(fields["blood_type"])
target.dna.temporary_mutations[UE_CHANGED] = endtime
if(fields["UI"]) //UI+UE
if(!target.dna.previous["UI"])
diff --git a/code/game/objects/items/food/meatslab.dm b/code/game/objects/items/food/meatslab.dm
index c195b76f1ca..d0b79db44fd 100644
--- a/code/game/objects/items/food/meatslab.dm
+++ b/code/game/objects/items/food/meatslab.dm
@@ -6,7 +6,7 @@
var/subjectjob = null
var/blood_decal_type = /obj/effect/decal/cleanable/blood
-/obj/item/food/meat/Initialize(mapload)
+/obj/item/food/meat/Initialize(mapload, blood_dna_list = list("meaty DNA" = get_blood_type(BLOOD_TYPE_MEAT)))
. = ..()
if(!blood_decal_type)
@@ -16,13 +16,14 @@
/datum/component/blood_walk,\
blood_type = blood_decal_type,\
blood_spawn_chance = 45,\
+ transfer_blood_dna = TRUE,\
max_blood = custom_materials[custom_materials[1]] / SHEET_MATERIAL_AMOUNT,\
)
AddComponent(
/datum/component/bloody_spreader,\
blood_left = custom_materials[custom_materials[1]] / SHEET_MATERIAL_AMOUNT,\
- blood_dna = list("meaty DNA" = "MT-"),\
+ blood_dna = blood_dna_list,\
diseases = null,\
)
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index 0c5dea81d54..e7c932ab2be 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -26,7 +26,7 @@
human.skin_tone = pick(GLOB.skin_tones)
human.dna.species.randomize_active_underwear_only(human)
// Needs to be called towards the end to update all the UIs just set above
- human.dna.initialize_dna(newblood_type = random_blood_type(), create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
+ human.dna.initialize_dna(newblood_type = random_human_blood_type(), create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
// SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION
human.dna.species.mutant_bodyparts = human.dna.mutant_bodyparts.Copy()
human.dna.species.body_markings = human.dna.body_markings.Copy()
@@ -65,7 +65,7 @@
if(facial_hair && facial_hair.natural_spawn && !facial_hair.locked)
human.set_facial_hairstyle(facial_hair.name, update = FALSE)
// Normal DNA init stuff, these can generally be wacky but we care less, they're aliens after all
- human.dna.initialize_dna(newblood_type = random_blood_type(), create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
+ human.dna.initialize_dna(newblood_type = random_human_blood_type(), create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
human.updatehealth()
if(update_body)
human.updateappearance(mutcolor_update = TRUE)
diff --git a/code/modules/admin/smites/cluwndice.dm b/code/modules/admin/smites/cluwndice.dm
new file mode 100644
index 00000000000..94355eb98db
--- /dev/null
+++ b/code/modules/admin/smites/cluwndice.dm
@@ -0,0 +1,14 @@
+/// Makes the target's blood a beautiful rainbow
+/datum/smite/clownify_blood
+ name = "Clownify blood"
+
+/datum/smite/clownify_blood/effect(client/user, mob/living/target)
+ . = ..()
+
+ if (!iscarbon(target))
+ to_chat(user, span_warning("This must be used on a carbon mob."), confidential = TRUE)
+ return
+
+ var/mob/living/carbon/carbon_target = target
+ carbon_target.set_blood_type(get_blood_type(BLOOD_TYPE_CLOWN))
+ SEND_SOUND(carbon_target, 'sound/items/bikehorn.ogg')
diff --git a/code/modules/admin/verbs/list_exposer.dm b/code/modules/admin/verbs/list_exposer.dm
index 3c0b6caa256..2f7e3d67d5f 100644
--- a/code/modules/admin/verbs/list_exposer.dm
+++ b/code/modules/admin/verbs/list_exposer.dm
@@ -36,7 +36,7 @@
for(var/entry in GLOB.human_list)
var/mob/living/carbon/human/subject = entry
if(subject.ckey)
- data += "
| [subject] | [subject.dna.unique_enzymes] | [subject.dna.blood_type] |
"
+ data += "| [subject] | [subject.dna.unique_enzymes] | [subject.dna.blood_type.name] |
"
data += ""
var/datum/browser/browser = new(usr, "DNA", "DNA Log", 440, 410)
diff --git a/code/modules/antagonists/abductor/equipment/glands/heal.dm b/code/modules/antagonists/abductor/equipment/glands/heal.dm
index 13761ba0cdb..9800b4fa308 100644
--- a/code/modules/antagonists/abductor/equipment/glands/heal.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/heal.dm
@@ -223,6 +223,7 @@
var/list/dirs = GLOB.alldirs.Copy()
for(var/i in 1 to 3)
var/obj/effect/decal/cleanable/blood/gibs/gibs = new(get_turf(owner))
+ gibs.add_blood_DNA(blood_dna_info)
gibs.streak(dirs)
var/obj/item/bodypart/chest/new_chest = new(null)
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index b9cf9354e44..c3725212ceb 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -145,11 +145,15 @@
brother2.set_species(/datum/species/moth)
var/icon/brother1_icon = render_preview_outfit(/datum/outfit/job/quartermaster, brother1)
- brother1_icon.Blend(icon('icons/effects/blood.dmi', "maskblood"), ICON_OVERLAY)
+ var/icon/brother1_blood_icon = icon('icons/effects/blood.dmi', "maskblood")
+ brother1_blood_icon.Blend(BLOOD_COLOR_RED, ICON_MULTIPLY)
+ brother1_icon.Blend(brother1_blood_icon, ICON_OVERLAY)
brother1_icon.Shift(WEST, 8)
var/icon/brother2_icon = render_preview_outfit(/datum/outfit/job/scientist/consistent, brother2)
- brother2_icon.Blend(icon('icons/effects/blood.dmi', "uniformblood"), ICON_OVERLAY)
+ var/icon/brother2_blood_icon = icon('icons/effects/blood.dmi', "uniformblood")
+ brother2_blood_icon.Blend(BLOOD_COLOR_RED, ICON_MULTIPLY)
+ brother2_icon.Blend(brother2_blood_icon, ICON_OVERLAY)
brother2_icon.Shift(EAST, 8)
var/icon/final_icon = brother1_icon
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index afb028e9947..b04def466a7 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -385,7 +385,7 @@
victim.visible_message(span_danger("[user] impales [victim] with [user.p_their()] [weapon.name]!"), span_userdanger("[user] impales you with [user.p_their()] [weapon.name]!"))
victim.apply_damage(weapon.force, BRUTE, BODY_ZONE_CHEST, attacking_item = weapon)
user.do_item_attack_animation(victim, used_item = weapon, animation_type = ATTACK_ANIMATION_PIERCE)
- user.add_mob_blood(victim)
+ user.add_blood_DNA_to_items(victim.get_blood_dna_list(), ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING)
playsound(get_turf(user),weapon.hitsound,75,TRUE)
return
diff --git a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm
index 523db5a3214..46d04d7a4e4 100644
--- a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm
+++ b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm
@@ -150,7 +150,7 @@
AddComponent(/datum/component/bloody_spreader,\
blood_left = INFINITY,\
- blood_dna = list("meaty DNA" = "MT-"),\
+ blood_dna = list("meaty DNA" = get_blood_type(BLOOD_TYPE_MEAT)),\
diseases = null,\
)
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index 41161b75c87..b59741c259a 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -137,7 +137,7 @@
AddComponent(
/datum/component/bloody_spreader,\
blood_left = INFINITY,\
- blood_dna = list("Unknown DNA" = "X*"),\
+ blood_dna = list("Unknown DNA" = BLOOD_TYPE_XENO),\
diseases = null,\
)
diff --git a/code/modules/antagonists/obsessed/obsessed.dm b/code/modules/antagonists/obsessed/obsessed.dm
index 483933b3b4a..7cb03b8b3a0 100644
--- a/code/modules/antagonists/obsessed/obsessed.dm
+++ b/code/modules/antagonists/obsessed/obsessed.dm
@@ -64,7 +64,9 @@
victim_dummy.set_hairstyle("Messy", update = TRUE)
var/icon/obsessed_icon = render_preview_outfit(preview_outfit)
- obsessed_icon.Blend(icon('icons/effects/blood.dmi', "uniformblood"), ICON_OVERLAY)
+ var/icon/blood_icon = icon('icons/effects/blood.dmi', "uniformblood")
+ blood_icon.Blend(BLOOD_COLOR_RED, ICON_MULTIPLY)
+ obsessed_icon.Blend(blood_icon, ICON_OVERLAY)
var/icon/final_icon = finish_preview_icon(obsessed_icon)
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 95cca7f2e5d..93fa2e6d0f1 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -56,7 +56,9 @@
if(isinhands)
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
- . += mutable_appearance('icons/effects/blood.dmi', "gloveblood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "gloveblood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
/obj/item/clothing/gloves/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
..()
diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm
index a8690c1f10c..c5c36a03537 100644
--- a/code/modules/clothing/head/_head.dm
+++ b/code/modules/clothing/head/_head.dm
@@ -67,9 +67,13 @@
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
if(clothing_flags & LARGE_WORN_ICON)
- . += mutable_appearance('icons/effects/64x64.dmi', "helmetblood_large")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/64x64.dmi', "helmetblood_large")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
else
- . += mutable_appearance('icons/effects/blood.dmi', "helmetblood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "helmetblood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
/obj/item/clothing/head/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
..()
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 35675909f17..9443730df49 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -38,7 +38,9 @@
if(isinhands || !(body_parts_covered & HEAD))
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
- . += mutable_appearance('icons/effects/blood.dmi', "maskblood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "maskblood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
/obj/item/clothing/mask/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
..()
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index ae4e6c6951b..5605cfd0827 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -19,7 +19,9 @@
if(isinhands || !(body_parts_covered & HEAD))
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
- . += mutable_appearance('icons/effects/blood.dmi', "maskblood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "maskblood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
/obj/item/clothing/neck/bowtie
name = "bow tie"
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index e1ddb1b431b..f5cbee74c41 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -66,9 +66,13 @@
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
if(clothing_flags & LARGE_WORN_ICON)
- . += mutable_appearance('icons/effects/64x64.dmi', "shoeblood_large")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/64x64.dmi', "shoeblood_large")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
else
- . += mutable_appearance('icons/effects/blood.dmi', "shoeblood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "shoeblood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
*/
//SKYRAT EDIT REMOVAL END
/obj/item/clothing/shoes/examine(mob/user)
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index 5932e16063f..df62e190ba6 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -51,7 +51,9 @@
if(isinhands)
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
- . += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
/obj/item/clothing/suit/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
..()
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 0dc059e5ae2..f5726555feb 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -108,7 +108,9 @@
if(isinhands)
return
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
- . += mutable_appearance('icons/effects/blood.dmi', "uniformblood")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "uniformblood")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ . += blood_overlay
/obj/item/clothing/under/attackby(obj/item/attacking_item, mob/user, list/modifiers)
if(repair_sensors(attacking_item, user))
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index 1da68e417e1..5c6e7b6bca7 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -7,12 +7,18 @@
circuit = /obj/item/circuitboard/machine/gibber
anchored_tabletop_offset = 8
- var/operating = FALSE //Is it on?
- var/dirty = FALSE // Does it need cleaning?
- var/gibtime = 40 // Time from starting until meat appears
+ //Is it on?
+ var/operating = FALSE
+ /// Does it need cleaning?
+ var/dirty = FALSE
+ /// Time from starting until meat appears
+ var/gibtime = 40
+ /// How much meat we meet when we meat the meat
var/meat_produced = 2
+ /// If the gibber should give the 'Subject may not have abiotic items on' message
var/ignore_clothing = FALSE
-
+ /// The DNA info of the last gibbed mob
+ var/blood_dna_info
/obj/machinery/gibber/Initialize(mapload)
. = ..()
@@ -48,7 +54,12 @@
/obj/machinery/gibber/update_overlays()
. = ..()
if(dirty)
- . += "grinder_bloody"
+ var/mutable_appearance/blood_overlay = mutable_appearance(icon, "grinder_bloody", appearance_flags = RESET_COLOR|KEEP_APART)
+ if(blood_dna_info)
+ blood_overlay.color = get_blood_dna_color(blood_dna_info)
+ else
+ blood_overlay.color = BLOOD_COLOR_RED
+ . += blood_overlay
if(machine_stat & (NOPOWER|BROKEN) || panel_open)
return
if(!occupant)
@@ -193,16 +204,18 @@
else if(gibee.dna && gibee.dna.species)
typeofmeat = gibee.dna.species.meat
typeofskin = gibee.dna.species.skinned_type
+ blood_dna_info = gibee.get_blood_dna_list()
else if(iscarbon(occupant))
- var/mob/living/carbon/C = occupant
- typeofmeat = C.type_of_meat
- gibtype = C.gib_type
- if(isalien(C))
+ var/mob/living/carbon/carbon_occupant = occupant
+ typeofmeat = carbon_occupant.type_of_meat
+ gibtype = carbon_occupant.gib_type
+ if(isalien(carbon_occupant))
typeofskin = /obj/item/stack/sheet/animalhide/xeno
+ blood_dna_info = carbon_occupant.get_blood_dna_list()
for (var/i in 1 to meat_produced)
- var/obj/item/food/meat/slab/newmeat = new typeofmeat
+ var/obj/item/food/meat/slab/newmeat = new typeofmeat(null, blood_dna_info)
newmeat.name = "[sourcename] [newmeat.name]"
newmeat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, occupant) = 4 * SHEET_MATERIAL_AMOUNT))
if(!istype(newmeat))
@@ -234,13 +247,15 @@
mob_occupant.ghostize()
set_occupant(null)
qdel(mob_occupant)
- addtimer(CALLBACK(src, PROC_REF(make_meat), skin, results, meat_produced, gibtype, diseases), gibtime)
+ addtimer(CALLBACK(src, PROC_REF(make_meat), skin, results, meat_produced, gibtype, diseases, blood_dna_info), gibtime)
-/obj/machinery/gibber/proc/make_meat(obj/item/stack/sheet/animalhide/skin, list/results, meat_produced, gibtype, list/datum/disease/diseases)
+/obj/machinery/gibber/proc/make_meat(obj/item/stack/sheet/animalhide/skin, list/results, meat_produced, gibtype, list/datum/disease/diseases, blood_dna_info)
playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE)
operating = FALSE
if (!dirty && prob(50))
dirty = TRUE
+ if(blood_dna_info)
+ add_blood_DNA(blood_dna_info)
var/turf/T = get_turf(src)
var/list/turf/nearby_turfs = RANGE_TURFS(3,T) - T
if(skin)
@@ -259,7 +274,8 @@
diseases_to_add += disease
if(LAZYLEN(diseases_to_add))
meatslab.AddComponent(/datum/component/infective, diseases_to_add)
-
+ if(blood_dna_info)
+ meatslab.add_blood_DNA(blood_dna_info)
meatslab.forceMove(loc)
meatslab.throw_at(pick(nearby_turfs), iteration, 3)
@@ -268,8 +284,9 @@
for (var/i in 1 to meat_produced**2) //2 slabs: 4 giblets, 3 slabs: 9, etc.
var/turf/gibturf = pick(nearby_turfs)
if (!gibturf.density && (src in view(gibturf)))
- new gibtype(gibturf, round(1 + i / meat_produced), diseases)
-
+ var/obj/effect/decal/cleanable/new_gibs = new gibtype(gibturf, round(1 + i / meat_produced), diseases)
+ if(blood_dna_info)
+ new_gibs.add_blood_DNA(blood_dna_info)
pixel_x = base_pixel_x //return to its spot after shaking
operating = FALSE
diff --git a/code/modules/forensics/_forensics.dm b/code/modules/forensics/_forensics.dm
index 450521e6c62..a6801d63dbf 100644
--- a/code/modules/forensics/_forensics.dm
+++ b/code/modules/forensics/_forensics.dm
@@ -231,4 +231,4 @@
return
if(!length(blood_DNA))
return
- parent.AddElement(/datum/element/decal/blood)
+ parent.AddElement(/datum/element/decal/blood, _color = get_blood_dna_color(blood_DNA))
diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm
index 8cb7f721842..e0edfafc9e6 100644
--- a/code/modules/forensics/forensics_helpers.dm
+++ b/code/modules/forensics/forensics_helpers.dm
@@ -109,24 +109,55 @@
/turf/closed/add_blood_DNA(list/blood_dna, list/datum/disease/diseases)
return FALSE
-/mob/living/carbon/human/add_blood_DNA(list/blood_DNA_to_add, list/datum/disease/diseases)
- if (QDELETED(src))
+/obj/item/clothing/under/add_blood_DNA(list/blood_DNA_to_add)
+ . = ..()
+ if(!.)
return
- if(wear_suit)
- wear_suit.add_blood_DNA(blood_DNA_to_add)
- update_worn_oversuit()
- else if(w_uniform)
- w_uniform.add_blood_DNA(blood_DNA_to_add)
- update_worn_undersuit()
- if(gloves)
- var/obj/item/clothing/gloves/mob_gloves = gloves
- mob_gloves.add_blood_DNA(blood_DNA_to_add)
- else if(length(blood_DNA_to_add))
- if (isnull(forensics))
+ for(var/obj/item/clothing/accessory/thing_accessory as anything in attached_accessories)
+ if(prob(66))
+ continue
+ thing_accessory.add_blood_DNA(blood_DNA_to_add)
+
+/mob/living/carbon/human/add_blood_DNA(list/blood_DNA_to_add, list/datum/disease/diseases)
+ return add_blood_DNA_to_items(blood_DNA_to_add)
+
+/// Adds blood DNA to certain slots the mob is wearing
+/mob/living/carbon/human/proc/add_blood_DNA_to_items(
+ list/blood_DNA_to_add,
+ target_flags = ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING|ITEM_SLOT_GLOVES|ITEM_SLOT_HEAD|ITEM_SLOT_MASK,
+)
+ if(QDELING(src))
+ return FALSE
+ if(!length(blood_DNA_to_add))
+ return FALSE
+
+ // Don't messy up our jumpsuit if we're got a coat
+ if((target_flags & ITEM_SLOT_OCLOTHING) && (wear_suit?.body_parts_covered & CHEST))
+ target_flags &= ~ITEM_SLOT_ICLOTHING
+
+ var/dirty_hands = !!(target_flags & (ITEM_SLOT_GLOVES|ITEM_SLOT_HANDS))
+ var/dirty_feet = !!(target_flags & ITEM_SLOT_FEET)
+ var/slots_to_bloody = target_flags & ~check_obscured_slots()
+ var/list/all_worn = get_equipped_items()
+ for(var/obj/item/thing as anything in all_worn)
+ if(thing.slot_flags & slots_to_bloody)
+ thing.add_blood_DNA(blood_DNA_to_add)
+ if(thing.body_parts_covered & HANDS)
+ dirty_hands = FALSE
+ if(thing.body_parts_covered & FEET)
+ dirty_feet = FALSE
+
+ if(slots_to_bloody & ITEM_SLOT_HANDS)
+ for(var/obj/item/thing in held_items)
+ thing.add_blood_DNA(blood_DNA_to_add)
+
+ if(dirty_hands || dirty_feet || !length(all_worn))
+ if(isnull(forensics))
forensics = new(src)
forensics.inherit_new(blood_DNA = blood_DNA_to_add)
- blood_in_hands = rand(2, 4)
- update_worn_gloves()
+ if(dirty_hands)
+ blood_in_hands = rand(2, 4)
+ update_clothing(slots_to_bloody)
return TRUE
/*
diff --git a/code/modules/hallucination/blood_flow.dm b/code/modules/hallucination/blood_flow.dm
index aaaba4ba2c9..71a58896bf3 100644
--- a/code/modules/hallucination/blood_flow.dm
+++ b/code/modules/hallucination/blood_flow.dm
@@ -9,7 +9,7 @@
return FALSE
var/mob/living/carbon/carb_hallucinator = hallucinator
- if(!length(carb_hallucinator.bodyparts) || HAS_TRAIT(carb_hallucinator, TRAIT_NOBLOOD))
+ if(!length(carb_hallucinator.bodyparts) || HAS_TRAIT(carb_hallucinator, TRAIT_NOBLOOD) || carb_hallucinator.dna?.blood_type.no_bleed_overlays)
return FALSE
var/obj/item/bodypart/picked
var/list/bodyparts = carb_hallucinator.bodyparts.Copy()
@@ -39,6 +39,7 @@
icon_state = "[picked.body_zone]_[pick(2, 3)]",
loc = hallucinator,
)
+ bleeding.color = carb_hallucinator.dna.blood_type.get_color()
bleeding.layer = -WOUND_LAYER
hallucinator.client?.images += bleeding
return TRUE
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index 03386e7d1ee..3602d5a9801 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -205,7 +205,16 @@
most_plentiful_reagent.Cut()
most_plentiful_reagent[reagent] = reagents_add[reagent]
- podman.dna.species.exotic_blood = most_plentiful_reagent[1]
+ var/datum/reagent/new_blood_reagent = most_plentiful_reagent[1]
+ podman.dna.species.exotic_blood = new_blood_reagent
+
+ // Try to find a corresponding blood type for this reagent
+ var/datum/blood_type/new_blood_type = get_blood_type(new_blood_reagent)
+ if(isnull(new_blood_type)) // this blood type doesn't exist yet in the global list, so make a new one
+ new_blood_type = new /datum/blood_type/random_chemical(new_blood_reagent)
+ GLOB.blood_types[new_blood_type::id] = new_blood_type
+ podman.set_blood_type(new_blood_type)
+
investigate_log("[key_name(mind)] cloned as a podman via [src] in [parent]", INVESTIGATE_BOTANY)
parent.update_tray(user, 1)
return result
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index e5edfb2c0ef..d0cb9870059 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -289,7 +289,7 @@
var/amount = max(1, round((edible_vol)*(potency/100) * reagent_overflow_mod, 1)) //the plant will always have at least 1u of each of the reagents in its reagent production traits
var/list/data
if(rid == /datum/reagent/blood) // Hack to make blood in plants always O-
- data = list("blood_type" = "O-")
+ data = list("blood_type" = get_blood_type(BLOOD_TYPE_O_MINUS))
if(istype(grown_edible) && (rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin))
data = grown_edible.tastes // apple tastes of apple.
T.reagents.add_reagent(rid, amount, data, added_purity = reagent_purity)
diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm
index 314f4931106..569e33728f2 100644
--- a/code/modules/jobs/job_types/clown.dm
+++ b/code/modules/jobs/job_types/clown.dm
@@ -41,6 +41,10 @@
/datum/job/clown/after_spawn(mob/living/spawned, client/player_client)
if (ishuman(spawned))
spawned.apply_pref_name(/datum/preference/name/clown, player_client)
+ if(check_holidays(APRIL_FOOLS)) // Clown blood is real
+ var/mob/living/carbon/human/human_clown = spawned
+ human_clown.set_blood_type(get_blood_type(BLOOD_TYPE_CLOWN))
+
return ..()
/datum/outfit/job/clown
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
index bd39621eb78..f46b2f78583 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
@@ -47,7 +47,7 @@
. = ..()
AddComponent(/datum/component/bloody_spreader,\
blood_left = INFINITY,\
- blood_dna = list("meaty DNA" = "MT-"),\
+ blood_dna = list("meaty DNA" = get_blood_type(BLOOD_TYPE_MEAT)),\
diseases = null,\
)
diff --git a/code/modules/mob/living/basic/alien/_alien.dm b/code/modules/mob/living/basic/alien/_alien.dm
index 59aca1021d4..14d74409016 100644
--- a/code/modules/mob/living/basic/alien/_alien.dm
+++ b/code/modules/mob/living/basic/alien/_alien.dm
@@ -85,3 +85,10 @@
return
visible_message(span_alertalien("[src] lays an egg!"))
new /obj/structure/alien/egg(loc)
+
+/mob/living/basic/alien/spawn_gibs(drop_bitflags=NONE)
+ if(drop_bitflags & DROP_BODYPARTS)
+ new /obj/effect/gibspawner/xeno(drop_location(), src)
+ else
+ new /obj/effect/gibspawner/xeno/bodypartless(drop_location(), src)
+
diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm
index ae66369dd93..513ffde549e 100644
--- a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm
+++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm
@@ -59,7 +59,7 @@
AddComponent(\
/datum/component/bloody_spreader,\
blood_left = INFINITY,\
- blood_dna = list("meaty DNA" = "MT-"),\
+ blood_dna = list("meaty DNA" = get_blood_type(BLOOD_TYPE_MEAT)),\
diseases = null,\
)
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 18ac35d6733..1f22d4baed8 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -252,7 +252,8 @@
if((blood_disease.spread_flags & DISEASE_SPREAD_SPECIAL) || (blood_disease.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS))
continue
carbon_receiver.ForceContractDisease(blood_disease)
- if(!(blood_data["blood_type"] in get_safe_blood(carbon_receiver.dna.blood_type)) && !(ignore_incompatibility))
+ var/datum/blood_type/blood_type = blood_data["blood_type"]
+ if(!ignore_incompatibility && !(blood_type.type_key() in carbon_receiver.dna.blood_type.compatible_types))
carbon_receiver.reagents.add_reagent(/datum/reagent/toxin, amount * 0.5)
return TRUE
@@ -322,42 +323,34 @@
return
return /datum/reagent/blood
-// This is has more potential uses, and is probably faster than the old proc.
-/proc/get_safe_blood(bloodtype)
- . = list()
- if(!bloodtype)
- return
+/// Returns the blood_type datum that corresponds to the string id key in GLOB.blood_types
+/proc/get_blood_type(id)
+ return GLOB.blood_types[id]
- var/static/list/bloodtypes_safe = list(
- "A-" = list("A-", "O-"),
- "A+" = list("A-", "A+", "O-", "O+"),
- "B-" = list("B-", "O-"),
- "B+" = list("B-", "B+", "O-", "O+"),
- "AB-" = list("A-", "B-", "O-", "AB-"),
- "AB+" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+"),
- "O-" = list("O-"),
- "O+" = list("O-", "O+"),
- "L" = list("L"),
- "U" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+", "L", "U")
- )
-
- var/safe = bloodtypes_safe[bloodtype]
- if(safe)
- . = safe
+/// Returns the hex color string of a given blood_type datum given an assoc list of blood_DNA e.g. ("Unknown Blood Type", "*X")
+/proc/get_blood_dna_color(list/blood_DNA)
+ var/datum/blood_type/blood_type
+ if(length(blood_DNA))
+ var/last_added_bloodtype_key = blood_DNA[length(blood_DNA)]
+ blood_type = blood_DNA[last_added_bloodtype_key]
+ if(!istype(blood_type))
+ blood_type = get_blood_type(blood_type) || random_human_blood_type()
+ return blood_type.get_color()
/**
* Returns TRUE if src is compatible with donor's blood, otherwise FALSE.
* * donor: Carbon mob, the one that is donating blood.
*/
/mob/living/carbon/proc/get_blood_compatibility(mob/living/carbon/donor)
- var/patient_blood_data = get_blood_data(get_blood_id())
- var/donor_blood_data = donor.get_blood_data(donor.get_blood_id())
- return donor_blood_data["blood_type"] in get_safe_blood(patient_blood_data["blood_type"])
+ var/datum/blood_type/patient_blood_data = dna.blood_type
+ var/datum/blood_type/donor_blood_data = donor.dna.blood_type
+ return (donor_blood_data in patient_blood_data.compatible_types)
//to add a splatter of blood or other mob liquid.
-/mob/living/proc/add_splatter_floor(turf/splatter_turf, small_drip)
- if(get_blood_id() != /datum/reagent/blood)
+/mob/living/proc/add_splatter_floor(turf/splatter_turf, small_drip, skip_reagents_check = FALSE)
+ if(!skip_reagents_check && !(get_blood_id() in list(/datum/reagent/blood, /datum/reagent/toxin/acid)))
return
+
if(!splatter_turf)
splatter_turf = get_turf(src)
if(isclosedturf(splatter_turf) || (isgroundlessturf(splatter_turf) && !GET_TURF_BELOW(splatter_turf)))
@@ -374,14 +367,14 @@
if(drop.drips < 5)
drop.drips++
drop.add_overlay(pick(drop.random_icon_states))
- drop.transfer_mob_blood_dna(src)
+ drop.add_mob_blood(src)
return
else
temp_blood_DNA = GET_ATOM_BLOOD_DNA(drop) //we transfer the dna from the drip to the splatter
qdel(drop)//the drip is replaced by a bigger splatter
else
drop = new(splatter_turf, get_static_viruses())
- drop.transfer_mob_blood_dna(src)
+ drop.add_mob_blood(src)
return
// Find a blood decal or create a new one.
@@ -391,23 +384,23 @@
if(QDELETED(blood_spew)) //Give it up
return
blood_spew.bloodiness = min((blood_spew.bloodiness + BLOOD_AMOUNT_PER_DECAL), BLOOD_POOL_MAX)
- blood_spew.transfer_mob_blood_dna(src) //give blood info to the blood decal.
+ blood_spew.add_mob_blood(src) //give blood info to the blood decal.
if(temp_blood_DNA)
- blood_spew.add_blood_DNA(temp_blood_DNA)
+ blood_spew.add_blood_DNA(temp_blood_DNA, no_visuals = small_drip)
-/mob/living/carbon/human/add_splatter_floor(turf/splatter_turf, small_drip)
- if(!HAS_TRAIT(src, TRAIT_NOBLOOD))
+/mob/living/carbon/human/add_splatter_floor(turf/splatter_turf, small_drip, skip_reagents_check = TRUE)
+ if(!HAS_TRAIT(src, TRAIT_NOBLOOD) && !dna?.blood_type.no_bleed_overlays)
. = ..()
-/mob/living/carbon/alien/add_splatter_floor(turf/splatter_turf, small_drip)
+/mob/living/carbon/alien/add_splatter_floor(turf/splatter_turf, small_drip, skip_reagents_check)
if(!splatter_turf)
splatter_turf = get_turf(src)
var/obj/effect/decal/cleanable/xenoblood/xeno_blood_splatter = locate() in splatter_turf.contents
if(!xeno_blood_splatter)
xeno_blood_splatter = new(splatter_turf)
- xeno_blood_splatter.add_blood_DNA(list("UNKNOWN DNA" = "X*"))
+ xeno_blood_splatter.add_blood_DNA(list("UNKNOWN DNA" = BLOOD_TYPE_XENO))
-/mob/living/silicon/robot/add_splatter_floor(turf/splatter_turf, small_drip)
+/mob/living/silicon/robot/add_splatter_floor(turf/splatter_turf, small_drip, skip_reagents_check)
if(!splatter_turf)
splatter_turf = get_turf(src)
var/obj/effect/decal/cleanable/oil/oil_splatter = locate() in splatter_turf.contents
diff --git a/code/modules/mob/living/blood_types.dm b/code/modules/mob/living/blood_types.dm
new file mode 100644
index 00000000000..031eb402f30
--- /dev/null
+++ b/code/modules/mob/living/blood_types.dm
@@ -0,0 +1,247 @@
+/datum/blood_type
+ /// Name of the blood type.
+ var/name = "?"
+ /// A description of the blood type.
+ var/desc
+ /// Unique identifier for the blood type in the global list of singletons. Typically this is just the name, but some blood types might have the same name (e.g. evil blood)
+ var/id
+ /// Shown color of the blood type.
+ var/color = BLOOD_COLOR_RED
+ /// Additional lightness multiplier for the blood color, useful for when the default lightness from the greyscaling doesn't cut it and you want something more vibrant.
+ /// When set, color will be transformed into a matrix with coefficients multiplied by this value
+ var/lightness_mult = null
+ /// The cached color matrix for blood with a lightness_mult. We only need to calculate this once since blood types are singletons
+ var/list/blood_color_matrix
+ /// Blood types that are safe to use with people that have this blood type (for blood transfusions)
+ var/compatible_types = list()
+ /// What reagent is represented by this blood type?
+ var/datum/reagent/reagent_type = /datum/reagent/blood
+ /// What chem is used to restore this blood type (outside of itself, of course)?
+ var/datum/reagent/restoration_chem = /datum/reagent/iron
+ /// Whether or not this blood type should create blood trails, blood sprays, etc
+ var/no_bleed_overlays
+ /// Exclude abstract root types from being initialized by defining them here
+ var/root_abstract_type
+ /// If this blood type is meant to persist across species changes
+ var/is_species_universal
+
+/datum/blood_type/New()
+ . = ..()
+ id = name
+ compatible_types |= type_key()
+
+/datum/blood_type/Destroy(force)
+ if(!force)
+ stack_trace("qdel called on blood type singleton! (use FORCE if necessary)")
+ return QDEL_HINT_LETMELIVE
+
+ return ..()
+
+/**
+ * Key used to identify this blood type in compatible_types
+ *
+ * Allows for more complex or dynamically generated blood types
+ */
+/datum/blood_type/proc/type_key()
+ return type
+
+/// Returns blood color or color matrix
+/// Useful when you want to have a blood color with values out of normal hex bounds for that acidic look
+/// set dynamic to TRUE to redo the matrix each time (e.g. for clown blood dynamically shifting each time)
+/datum/blood_type/proc/get_color(dynamic = FALSE)
+ if(isnull(lightness_mult))
+ return color
+
+ if(!isnull(blood_color_matrix) && !dynamic)
+ return blood_color_matrix
+
+ blood_color_matrix = color_to_full_rgba_matrix(color)
+ for(var/i in 1 to min(length(blood_color_matrix), 16))
+ if (length(blood_color_matrix) == 12 && i > 9) // Don't modify constants
+ break
+ if (length(blood_color_matrix) >= 16 && i % 4 == 0) // Don't modify alpha either
+ continue
+ blood_color_matrix[i] *= lightness_mult
+
+ return blood_color_matrix
+
+// human blood type, for organizational purposes mainly
+/datum/blood_type/human
+ desc = "Blood cells suspended in plasma, the most abundant of which being the hemoglobin-containing red blood cells."
+ root_abstract_type = /datum/blood_type/human
+
+/datum/blood_type/human/a_minus
+ name = BLOOD_TYPE_A_MINUS
+ compatible_types = list(/datum/blood_type/human/a_minus, /datum/blood_type/human/o_minus)
+
+/datum/blood_type/human/a_plus
+ name = BLOOD_TYPE_A_PLUS
+ compatible_types = list(/datum/blood_type/human/a_minus, /datum/blood_type/human/a_plus, /datum/blood_type/human/o_minus, /datum/blood_type/human/o_plus)
+
+/datum/blood_type/human/b_minus
+ name = BLOOD_TYPE_B_MINUS
+ compatible_types = list(
+ /datum/blood_type/human/b_minus,
+ /datum/blood_type/human/o_minus,
+ )
+
+/datum/blood_type/human/b_plus
+ name = BLOOD_TYPE_B_PLUS
+ compatible_types = list(
+ /datum/blood_type/human/b_minus,
+ /datum/blood_type/human/b_plus,
+ /datum/blood_type/human/o_minus,
+ /datum/blood_type/human/o_plus,
+ )
+
+/datum/blood_type/human/ab_minus
+ name = BLOOD_TYPE_AB_MINUS
+ compatible_types = list(
+ /datum/blood_type/human/a_minus,
+ /datum/blood_type/human/b_minus,
+ /datum/blood_type/human/ab_minus,
+ /datum/blood_type/human/o_minus,
+ )
+
+/datum/blood_type/human/ab_plus
+ name = BLOOD_TYPE_AB_PLUS
+ compatible_types = list(
+ /datum/blood_type/human/a_minus,
+ /datum/blood_type/human/a_plus,
+ /datum/blood_type/human/b_minus,
+ /datum/blood_type/human/b_plus,
+ /datum/blood_type/human/o_minus,
+ /datum/blood_type/human/o_plus,
+ /datum/blood_type/human/ab_minus,
+ /datum/blood_type/human/ab_plus,
+ )
+
+/datum/blood_type/human/o_minus
+ name = BLOOD_TYPE_O_MINUS
+ compatible_types = list(
+ /datum/blood_type/human/o_minus,
+ )
+
+/datum/blood_type/human/o_plus
+ name = BLOOD_TYPE_O_PLUS
+ compatible_types = list(
+ /datum/blood_type/human/o_minus,
+ /datum/blood_type/human/o_plus,
+ )
+
+/datum/blood_type/animal
+ name = BLOOD_TYPE_ANIMAL
+ compatible_types = list(
+ /datum/blood_type/animal,
+ )
+
+/datum/blood_type/lizard
+ name = BLOOD_TYPE_LIZARD
+ compatible_types = list(
+ /datum/blood_type/lizard,
+ )
+
+/datum/blood_type/ethereal
+ name = BLOOD_TYPE_ETHEREAL
+ color = /datum/reagent/consumable/liquidelectricity::color
+ lightness_mult = 1.255 // for more vibrant gatorade coloring
+ compatible_types = list(
+ /datum/blood_type/ethereal,
+ )
+
+/datum/blood_type/oil
+ name = BLOOD_TYPE_OIL
+ color = BLOOD_COLOR_OIL
+ reagent_type = /datum/reagent/fuel/oil
+
+/datum/blood_type/vampire
+ name = BLOOD_TYPE_VAMPIRE
+ compatible_types = list(
+ /datum/blood_type/vampire,
+ )
+
+/datum/blood_type/meat // why does this exist
+ name = BLOOD_TYPE_MEAT
+
+/datum/blood_type/universal
+ name = BLOOD_TYPE_UNIVERSAL
+
+/datum/blood_type/universal/New()
+ . = ..()
+ compatible_types = subtypesof(/datum/blood_type)
+
+/datum/blood_type/xeno
+ name = BLOOD_TYPE_XENO
+ color = BLOOD_COLOR_XENO
+ lightness_mult = 1.255 // For parity with pre-refactor xeno blood sprites
+ compatible_types = list(/datum/blood_type/xeno)
+
+/// April fool's blood for clowns
+/datum/blood_type/clown
+ name = BLOOD_TYPE_CLOWN
+ reagent_type = /datum/reagent/colorful_reagent
+ lightness_mult = 1.255
+ is_species_universal = TRUE
+ /// The cached list of random colors to pick from
+ var/list/random_color_list
+
+/datum/blood_type/clown/get_color(dynamic = TRUE)
+ // Set up the random color list if we haven't done that yet. Only need to do this once.
+ if(isnull(random_color_list))
+ var/datum/reagent/colorful_reagent/clown_blood = new
+ random_color_list = clown_blood.random_color_list.Copy()
+ qdel(clown_blood)
+
+ color = pick(random_color_list)
+ return ..()
+
+/// Slimeperson blood, aka 'toxin' blood type
+/datum/blood_type/slime
+ name = BLOOD_TYPE_TOX
+ color = /datum/reagent/toxin/slimejelly::color
+ reagent_type = /datum/reagent/toxin/slimejelly
+ no_bleed_overlays = TRUE
+
+/// Podpeople blood
+/datum/blood_type/water
+ name = BLOOD_TYPE_H2O
+ color = /datum/reagent/water::color
+ reagent_type = /datum/reagent/water
+ no_bleed_overlays = TRUE
+
+/// Snail blood
+/datum/blood_type/snail
+ name = BLOOD_TYPE_SNAIL
+ reagent_type = /datum/reagent/lube
+
+/// An abstract-ish blood type used particularly for species with blood set to random reagents, such as podpeople
+/datum/blood_type/random_chemical
+ root_abstract_type = /datum/blood_type/random_chemical
+
+/datum/blood_type/random_chemical/New(datum/reagent/reagent_type)
+ . = ..()
+ src.name = initial(reagent_type.name)
+ src.color = initial(reagent_type.color)
+ src.reagent_type = reagent_type
+ src.restoration_chem = reagent_type
+ src.root_abstract_type = null
+
+/datum/blood_type/random_chemical/type_key()
+ return reagent_type
+
+// Similar to the random reagents bloodtype, this one creates a 'but evil' bloodtype
+/datum/blood_type/evil
+ root_abstract_type = /datum/blood_type/evil
+
+/datum/blood_type/evil/New(datum/blood_type/real_blood_type, list/real_compatible_types)
+ src.name = real_blood_type.name
+ . = ..()
+ id = type_key()
+ src.color = BLOOD_COLOR_BLACK // why it gotta be black though
+ src.reagent_type = real_blood_type.reagent_type
+ src.restoration_chem = real_blood_type.reagent_type
+ src.compatible_types = LAZYCOPY(real_compatible_types)
+ src.root_abstract_type = null
+
+/datum/blood_type/evil/type_key()
+ return "[name]_but_evil"
diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm
index ccce0269e54..54fbb132db2 100644
--- a/code/modules/mob/living/brain/brain.dm
+++ b/code/modules/mob/living/brain/brain.dm
@@ -9,7 +9,7 @@
/mob/living/brain/Initialize(mapload)
. = ..()
create_dna(src)
- stored_dna.initialize_dna(random_blood_type())
+ stored_dna.initialize_dna(random_human_blood_type())
if(isturf(loc)) //not spawned in an MMI or brain organ (most likely adminspawned)
var/obj/item/organ/brain/OB = new(loc) //we create a new brain organ for it.
OB.brainmob = src
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 917ab423864..7ea50789ab6 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1481,12 +1481,33 @@
/mob/living/carbon/proc/spray_blood(splatter_direction, splatter_strength = 3)
if(!isturf(loc))
return
+ if(dna.blood_type.no_bleed_overlays)
+ return
var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc)
our_splatter.add_blood_DNA(GET_ATOM_BLOOD_DNA(src))
our_splatter.blood_dna_info = get_blood_dna_list()
+ our_splatter.color = get_blood_dna_color(our_splatter.blood_dna_info)
var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)
our_splatter.fly_towards(targ, splatter_strength)
+/// Goes through the organs and bodyparts of the mob and updates their blood_dna_info, in case their blood type has changed (via set_species() or otherwise)
+/mob/living/carbon/proc/update_cached_blood_dna_info()
+ var/list/blood_dna_info = get_blood_dna_list()
+ for(var/obj/item/organ/organ in organs)
+ organ.blood_dna_info = blood_dna_info
+ for(var/obj/item/bodypart/bodypart in bodyparts)
+ bodypart.blood_dna_info = blood_dna_info
+
+/mob/living/carbon/set_blood_type(datum/blood_type/new_blood_type, update_cached_blood_dna_info = TRUE)
+ if(isnull(dna))
+ return
+ if(dna.blood_type == new_blood_type) // already has this blood type, we don't need to do anything.
+ return
+
+ dna.blood_type = new_blood_type
+ if(update_cached_blood_dna_info)
+ update_cached_blood_dna_info()
+
/mob/living/carbon/dropItemToGround(obj/item/item, force = FALSE, silent = FALSE, invdrop = TRUE)
if(item && ((item in organs) || (item in bodyparts))) //let's not do this, aight?
return FALSE
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index c6f41d420b5..9b89e649aab 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -289,6 +289,23 @@
else
Knockdown(stun_duration)
+/// When another mob touches us, they may messy us up.
+/mob/living/carbon/proc/share_blood_on_touch(mob/living/carbon/human/who_touched_us)
+ return
+
+/mob/living/carbon/human/share_blood_on_touch(mob/living/carbon/human/who_touched_us, messy_slots = ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING)
+ if(!istype(who_touched_us) || !messy_slots)
+ return
+
+ for(var/obj/item/thing as anything in who_touched_us.get_equipped_items())
+ if((thing.body_parts_covered & HANDS) && prob(GET_ATOM_BLOOD_DNA_LENGTH(thing) * 25))
+ add_blood_DNA_to_items(GET_ATOM_BLOOD_DNA(who_touched_us.wear_suit), messy_slots)
+ return
+
+ if(prob(blood_in_hands * GET_ATOM_BLOOD_DNA_LENGTH(who_touched_us) * 10))
+ add_blood_DNA_to_items(GET_ATOM_BLOOD_DNA(who_touched_us), messy_slots)
+ who_touched_us.blood_in_hands -= 1
+
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/helper, force_friendly)
var/nosound = FALSE //SKYRAT EDIT ADDITION - EMOTES
if(on_fire)
@@ -338,6 +355,7 @@
to_chat(helper, span_notice("You give [src] a pat on the head to make [p_them()] feel better!"))
to_chat(src, span_notice("[helper] gives you a pat on the head to make you feel better! "))
+ share_blood_on_touch(helper, ITEM_SLOT_HEAD|ITEM_SLOT_MASK)
if(HAS_TRAIT(src, TRAIT_BADTOUCH))
to_chat(helper, span_warning("[src] looks visibly upset as you pat [p_them()] on the head."))
//SKYRAT EDIT ADDITION BEGIN - EMOTES
@@ -385,6 +403,7 @@
to_chat(helper, span_notice("You hug [src] to make [p_them()] feel better!"))
to_chat(src, span_notice("[helper] hugs you to make you feel better!"))
+ share_blood_on_touch(helper, ITEM_SLOT_HEAD|ITEM_SLOT_MASK|ITEM_SLOT_GLOVES)
// Warm them up with hugs
share_bodytemperature(helper)
@@ -753,4 +772,7 @@
if(HAS_TRAIT(src, TRAIT_NO_SIDE_KICK)) // added as an extra check, just in case
. &= ~SHOVE_CAN_KICK_SIDE
+/mob/living/carbon/create_splatter(splatter_dir)
+ new /obj/effect/temp_visual/dir_setting/bloodsplatter(get_turf(src), splatter_dir, dna?.blood_type.get_color())
+
#undef SHAKE_ANIMATION_OFFSET
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 64455eacaf9..b7b55eeaffe 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -302,7 +302,11 @@
damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "blank", -DAMAGE_LAYER, appearance_flags = KEEP_TOGETHER)
damage_overlay.color = iter_part.damage_overlay_color
if(iter_part.brutestate)
- damage_overlay.add_overlay("[iter_part.dmg_overlay_type]_[iter_part.body_zone]_[iter_part.brutestate]0") //we're adding icon_states of the base image as overlays
+ var/mutable_appearance/blood_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[iter_part.dmg_overlay_type]_[iter_part.body_zone]_[iter_part.brutestate]0", appearance_flags = RESET_COLOR) //we're adding icon_states of the base image as overlays
+ blood_damage_overlay.color = dna.blood_type.get_color()
+ var/mutable_appearance/brute_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[iter_part.dmg_overlay_type]_[iter_part.body_zone]_[iter_part.brutestate]0_overlay", appearance_flags = RESET_COLOR)
+ blood_damage_overlay.overlays += brute_damage_overlay
+ damage_overlay.add_overlay(blood_damage_overlay)
if(iter_part.burnstate)
damage_overlay.add_overlay("[iter_part.dmg_overlay_type]_[iter_part.body_zone]_0[iter_part.burnstate]")
@@ -315,10 +319,15 @@
/mob/living/carbon/update_wound_overlays()
remove_overlay(WOUND_LAYER)
+ if(dna?.blood_type.no_bleed_overlays)
+ return
+
var/mutable_appearance/wound_overlay
for(var/obj/item/bodypart/iter_part as anything in bodyparts)
if(iter_part.bleed_overlay_icon)
- wound_overlay ||= mutable_appearance('icons/mob/effects/bleed_overlays.dmi', "blank", -WOUND_LAYER, appearance_flags = KEEP_TOGETHER)
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/mob/effects/bleed_overlays.dmi', "blank", -WOUND_LAYER, appearance_flags = KEEP_TOGETHER)
+ blood_overlay.color = dna.blood_type.get_color()
+ wound_overlay ||= blood_overlay
wound_overlay.add_overlay(iter_part.bleed_overlay_icon)
if(isnull(wound_overlay))
@@ -516,7 +525,7 @@
var/list/new_limbs = list()
for(var/obj/item/bodypart/limb as anything in bodyparts)
if(limb in needs_update)
- var/bodypart_icon = limb.get_limb_icon()
+ var/bodypart_icon = limb.get_limb_icon(dropped = FALSE, update_on = src)
new_limbs += bodypart_icon
limb_icon_cache[icon_render_keys[limb.body_zone]] = bodypart_icon //Caches the icon with the bodypart key, as it is new
else
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index e085e2a88bd..6cf5adae8d1 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -395,20 +395,24 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(old_species.type != type)
replace_body(human_who_gained_species, src)
+ if(!human_who_gained_species.dna.blood_type.is_species_universal) // Clown blood is forever.
+ //Assigns exotic blood type if the species has one
+ if(exotic_bloodtype && human_who_gained_species.dna.blood_type != exotic_bloodtype)
+ human_who_gained_species.set_blood_type(get_blood_type(exotic_bloodtype))
+ // updates the cached organ blood types in case our blood type changed
+ human_who_gained_species.update_cached_blood_dna_info()
+ //Otherwise, check if the previous species had an exotic bloodtype and we do not have one and assign a random blood type
+ //(why the fuck is blood type not tied to a fucking DNA block?)
+ else if(old_species.exotic_bloodtype && isnull(exotic_bloodtype))
+ human_who_gained_species.set_blood_type(random_human_blood_type())
+ human_who_gained_species.update_cached_blood_dna_info()
+
regenerate_organs(human_who_gained_species, old_species, replace_current = human_who_gained_species.visual_only_organs, visual_only = human_who_gained_species.visual_only_organs) // SKYRAT EDIT - Allows existing organs to be properly removed when regenerating organs - ORIGINAL: regenerate_organs(human_who_gained_species, old_species, replace_current = FALSE, visual_only = human_who_gained_species.visual_only_organs)
// Update locked slots AFTER all organ and body stuff is handled
human_who_gained_species.hud_used?.update_locked_slots()
// Drop the items the new species can't wear
INVOKE_ASYNC(src, PROC_REF(worn_items_fit_body_check), human_who_gained_species, TRUE)
- //Assigns exotic blood type if the species has one
- if(exotic_bloodtype && human_who_gained_species.dna.blood_type != exotic_bloodtype)
- human_who_gained_species.dna.blood_type = exotic_bloodtype
- //Otherwise, check if the previous species had an exotic bloodtype and we do not have one and assign a random blood type
- //(why the fuck is blood type not tied to a fucking DNA block?)
- else if(old_species.exotic_bloodtype && !exotic_bloodtype)
- human_who_gained_species.dna.blood_type = random_blood_type()
-
//Resets blood if it is excessively high so they don't gib
normalize_blood(human_who_gained_species)
@@ -787,10 +791,16 @@ GLOBAL_LIST_EMPTY(features_by_species)
**/
/datum/species/proc/handle_chemical(datum/reagent/chem, mob/living/carbon/human/affected, seconds_per_tick, times_fired)
SHOULD_CALL_PARENT(TRUE)
- if(chem.type == exotic_blood)
- affected.blood_volume = min(affected.blood_volume + round(chem.volume, 0.1), BLOOD_VOLUME_MAXIMUM)
- affected.reagents.del_reagent(chem.type)
- return COMSIG_MOB_STOP_REAGENT_CHECK
+ if(!istype(chem, /datum/reagent/blood)) // the blood reagent handles this itself, this is for exotic blood types
+ var/datum/blood_type/blood_type = affected.dna.blood_type
+ if(chem.type == blood_type?.reagent_type)
+ affected.blood_volume = min(affected.blood_volume + round(chem.volume, 0.1), BLOOD_VOLUME_MAXIMUM)
+ affected.reagents.del_reagent(chem.type)
+ return COMSIG_MOB_STOP_REAGENT_CHECK
+ if(chem.type == blood_type?.restoration_chem && affected.blood_volume < BLOOD_VOLUME_NORMAL)
+ affected.blood_volume += BLOOD_REGEN_FACTOR * seconds_per_tick
+ affected.reagents.remove_reagent(chem.type, chem.metabolization_rate * seconds_per_tick)
+ return COMSIG_MOB_STOP_REAGENT_CHECK
if(!chem.overdosed && chem.overdose_threshold && chem.volume >= chem.overdose_threshold && !HAS_TRAIT(affected, TRAIT_OVERDOSEIMMUNE))
chem.overdosed = TRUE
chem.overdose_start(affected)
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index d1fb4596dec..23c2516ecd3 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -122,7 +122,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
target.dna.features["pod_hair"] = get_consistent_feature_entry(SSaccessories.pod_hair_list)
target.dna.features["caps"] = get_consistent_feature_entry(SSaccessories.caps_list)
*/ // SKYRAT EDIT REMOVAL END
- target.dna.initialize_dna(create_mutation_blocks = FALSE, randomize_features = FALSE)
+ target.dna.initialize_dna(newblood_type = get_blood_type(BLOOD_TYPE_O_MINUS), create_mutation_blocks = FALSE, randomize_features = FALSE)
// UF and UI are nondeterministic, even though the features are the same some blocks will randomize slightly
// In practice this doesn't matter, but this is for the sake of 100%(ish) consistency
var/static/consistent_UF
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 40c6f6df473..1abb299631c 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -26,7 +26,7 @@
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(clean_face))
AddComponent(/datum/component/personal_crafting, ui_human_crafting)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6)
- AddComponent(/datum/component/bloodysoles/feet, FOOTPRINT_SPRITE_SHOES)
+ AddComponent(/datum/component/bloodysoles/feet)
AddElement(/datum/element/ridable, /datum/component/riding/creature/human)
AddElement(/datum/element/strippable, GLOB.strippable_human_items, TYPE_PROC_REF(/mob/living/carbon/human/, should_strip))
var/static/list/loc_connections = list(
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 293f2d54af3..6c954104923 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -208,9 +208,13 @@ There are several things that need to be remembered:
// When byond gives us filters that respect dirs we can just use an alpha mask for this but until then, two icons weeeee
var/mutable_appearance/hands_combined = mutable_appearance(layer = -GLOVES_LAYER, appearance_flags = KEEP_TOGETHER)
if(has_left_hand(check_disabled = FALSE))
- hands_combined.overlays += mutable_appearance('icons/effects/blood.dmi', "bloodyhands_left")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands_left")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ hands_combined.overlays += blood_overlay
if(has_right_hand(check_disabled = FALSE))
- hands_combined.overlays += mutable_appearance('icons/effects/blood.dmi', "bloodyhands_right")
+ var/mutable_appearance/blood_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands_right")
+ blood_overlay.color = get_blood_dna_color(GET_ATOM_BLOOD_DNA(src))
+ hands_combined.overlays += blood_overlay
overlays_standing[GLOVES_LAYER] = hands_combined
apply_overlay(GLOVES_LAYER)
return
@@ -1238,7 +1242,7 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E
my_head.update_limb(is_creating = update_limb_data)
- add_overlay(my_head.get_limb_icon())
+ add_overlay(my_head.get_limb_icon(dropped = FALSE, update_on = src))
update_worn_head()
update_worn_mask()
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index 1bceca3543e..b98e5fc77cf 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -8,7 +8,7 @@
mutanttongue = /obj/item/organ/tongue/ethereal
mutantheart = /obj/item/organ/heart/ethereal
exotic_blood = /datum/reagent/consumable/liquidelectricity //Liquid Electricity. fuck you think of something better gamer
- exotic_bloodtype = "LE"
+ exotic_bloodtype = BLOOD_TYPE_ETHEREAL
siemens_coeff = 0.5 //They thrive on energy
payday_modifier = 1.0
inherent_traits = list(
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index e8c62e1b340..7dbfab5011c 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -23,6 +23,7 @@
mutantheart = null
meat = /obj/item/food/meat/slab/human/mutant/slime
exotic_blood = /datum/reagent/toxin/slimejelly
+ exotic_bloodtype = BLOOD_TYPE_TOX
blood_deficiency_drain_rate = JELLY_REGEN_RATE + BLOOD_DEFICIENCY_MODIFIER
coldmod = 6 // = 3x cold damage
heatmod = 0.5 // = 1/4x heat damage
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index d935b0863c4..4927f55130e 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -24,7 +24,7 @@
species_cookie = /obj/item/food/meat/slab
meat = /obj/item/food/meat/slab/human/mutant/lizard
skinned_type = /obj/item/stack/sheet/animalhide/lizard
- exotic_bloodtype = "L"
+ exotic_bloodtype = BLOOD_TYPE_LIZARD
inert_mutation = /datum/mutation/human/firebreath
death_sound = 'sound/mobs/humanoids/lizard/deathsound.ogg'
species_language_holder = /datum/language_holder/lizard
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index 4c58a96eef6..1bb39cce4f3 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -17,6 +17,7 @@
payday_modifier = 1.0
meat = /obj/item/food/meat/slab/human/mutant/plant
exotic_blood = /datum/reagent/water
+ exotic_bloodtype = BLOOD_TYPE_H2O
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT
species_language_holder = /datum/language_holder/plant
diff --git a/code/modules/mob/living/carbon/human/species_types/snail.dm b/code/modules/mob/living/carbon/human/species_types/snail.dm
index edeb28f507e..da4a57cd375 100644
--- a/code/modules/mob/living/carbon/human/species_types/snail.dm
+++ b/code/modules/mob/living/carbon/human/species_types/snail.dm
@@ -17,6 +17,7 @@
mutanteyes = /obj/item/organ/eyes/snail
mutanttongue = /obj/item/organ/tongue/snail
exotic_blood = /datum/reagent/lube
+ exotic_bloodtype = BLOOD_TYPE_SNAIL
bodypart_overrides = list(
BODY_ZONE_HEAD = /obj/item/bodypart/head/snail,
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index e2ffbfccd3c..99e869c1cf8 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -18,7 +18,7 @@
)
inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID
changesource_flags = MIRROR_BADMIN | MIRROR_PRIDE | WABBAJACK | ERT_SPAWN
- exotic_bloodtype = "V"
+ exotic_bloodtype = BLOOD_TYPE_VAMPIRE
blood_deficiency_drain_rate = BLOOD_DEFICIENCY_MODIFIER // vampires already passively lose blood, so this just makes them lose it slightly more quickly when they have blood deficiency.
mutantheart = /obj/item/organ/heart/vampire
mutanttongue = /obj/item/organ/tongue/vampire
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index e793d02a7de..e894d38b17e 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -570,7 +570,7 @@
dna.unique_enzymes = dna.previous["UE"]
dna.previous.Remove("UE")
if(dna.previous["blood_type"])
- dna.blood_type = dna.previous["blood_type"]
+ set_blood_type(dna.previous["blood_type"])
dna.previous.Remove("blood_type")
dna.temporary_mutations.Remove(mut)
continue
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 33c4682348e..40def8e0915 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -455,6 +455,7 @@
span_warning("[src] grabs you [grabbed_by_hands ? "by your hands":"passively"]!"), null, null, src)
to_chat(src, span_notice("You grab [M] [grabbed_by_hands ? "by their hands":"passively"]!"))
// SKYRAT EDIT END
+ grabbed_human.share_blood_on_touch(src, grabbed_by_hands ? ITEM_SLOT_GLOVES : ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING)
else
M.visible_message(span_warning("[src] grabs [M] passively!"), \
span_warning("[src] grabs you passively!"), null, null, src)
@@ -1150,7 +1151,7 @@
trail.existing_dirs += newdir
trail.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir))
- trail.transfer_mob_blood_dna(src)
+ trail.add_mob_blood(src)
trail.bloodiness = min(trail.bloodiness + bleed_amount, BLOOD_POOL_MAX)
found_trail = TRUE
break
@@ -1162,14 +1163,14 @@
trail.blood_state = trail_blood_type
trail.existing_dirs += newdir
trail.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir))
- trail.transfer_mob_blood_dna(src)
+ trail.add_mob_blood(src)
trail.bloodiness = min(bleed_amount, BLOOD_POOL_MAX)
/mob/living/proc/get_trail_blood()
return BLOOD_STATE_HUMAN
/mob/living/carbon/human/makeTrail(turf/T)
- if(HAS_TRAIT(src, TRAIT_NOBLOOD) || !is_bleeding() || HAS_TRAIT(src, TRAIT_NOBLOOD))
+ if(HAS_TRAIT(src, TRAIT_NOBLOOD) || !is_bleeding() || dna.blood_type.no_bleed_overlays)
return
..()
@@ -3127,3 +3128,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
if(HAS_TRAIT(src, TRAIT_ANALGESIA) && !force)
return
INVOKE_ASYNC(src, PROC_REF(emote), "scream")
+
+/// Setter for changing a mob's blood type
+/mob/living/proc/set_blood_type(datum/blood_type/new_blood_type, update_cached_blood_dna_info)
+ return
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 837db2ea81f..f7be3dc517b 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -59,8 +59,11 @@
if(iscarbon(exposed_mob))
var/mob/living/carbon/exposed_carbon = exposed_mob
- if(exposed_carbon.get_blood_id() == type && ((methods & INJECT) || ((methods & INGEST) && HAS_TRAIT(exposed_carbon, TRAIT_DRINKS_BLOOD))))
- if(!data || !(data["blood_type"] in get_safe_blood(exposed_carbon.dna.blood_type)))
+ var/datum/blood_type/carbon_blood_type = exposed_carbon.dna.blood_type
+ if(carbon_blood_type.reagent_type == type && ((methods & INJECT) || ((methods & INGEST) && HAS_TRAIT(exposed_carbon, TRAIT_DRINKS_BLOOD))))
+ var/datum/blood_type/recipient_blood_type = exposed_carbon.dna.blood_type
+ var/datum/blood_type/donor_blood_type = data["blood_type"]
+ if(!(donor_blood_type.type_key() in recipient_blood_type.compatible_types))
exposed_carbon.reagents.add_reagent(/datum/reagent/toxin, reac_volume * 0.5)
else
/* SKYRAT EDIT - Rebalancing blood for Hemophages - ORIGINAL:
@@ -79,12 +82,19 @@
if(data["blood_DNA"] && data["blood_type"])
exposed_carbon.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
else
- exposed_carbon.add_blood_DNA(list("Non-human DNA" = random_blood_type()))
+ exposed_carbon.add_blood_DNA(list("Non-human DNA" = random_human_blood_type()))
/datum/reagent/blood/on_new(list/data)
. = ..()
- if(istype(data))
- SetViruses(src, data)
+ if(!istype(data))
+ return
+ SetViruses(src, data)
+ var/datum/blood_type/blood_type = data["blood_type"]
+ if(!blood_type)
+ return
+ var/blood_color = blood_type.get_color()
+ if(blood_color != BLOOD_COLOR_RED) // If the blood is default red, just use the darker red color for the reagent.
+ color = blood_color
/datum/reagent/blood/on_merge(list/mix_data)
if(data && mix_data)
@@ -159,7 +169,7 @@
if(data["blood_DNA"] && data["blood_type"])
exposed_obj.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
else
- exposed_obj.add_blood_DNA(list("Non-human DNA" = random_blood_type()))
+ exposed_obj.add_blood_DNA(list("Non-human DNA" = random_human_blood_type()))
/datum/reagent/blood/get_taste_description(mob/living/taster)
if(isnull(taster))
@@ -1236,11 +1246,6 @@
color = "#606060" //pure iron? let's make it violet of course
ph = 6
-/datum/reagent/iron/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
- . = ..()
- if(affected_mob.blood_volume < BLOOD_VOLUME_NORMAL)
- affected_mob.blood_volume += BLOOD_REGEN_FACTOR * seconds_per_tick
-
/datum/reagent/gold
name = "Gold"
description = "Gold is a dense, soft, shiny metal and the most malleable and ductile metal known."
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index 994eaa5ab99..8349141e32a 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -12,22 +12,23 @@
/obj/item/reagent_containers/blood/Initialize(mapload, vol)
. = ..()
if(blood_type != null)
- reagents.add_reagent(unique_blood ? unique_blood : /datum/reagent/blood, 200, list("viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null))
+ reagents.add_reagent(unique_blood ? unique_blood : /datum/reagent/blood, 200, list("viruses"=null,"blood_DNA"=null,"blood_type"=get_blood_type(blood_type),"resistances"=null,"trace_chem"=null))
update_appearance()
/// Handles updating the container when the reagents change.
/obj/item/reagent_containers/blood/on_reagent_change(datum/reagents/holder, ...)
var/datum/reagent/blood/new_reagent = holder.has_reagent(/datum/reagent/blood)
if(new_reagent && new_reagent.data && new_reagent.data["blood_type"])
- blood_type = new_reagent.data["blood_type"]
+ var/datum/blood_type/blood_type = new_reagent.data["blood_type"]
+ blood_type = blood_type.name
else if(holder.has_reagent(/datum/reagent/consumable/liquidelectricity))
- blood_type = "LE"
+ blood_type = BLOOD_TYPE_ETHEREAL
else if(holder.has_reagent(/datum/reagent/lube))
- blood_type = "S"
+ blood_type = BLOOD_TYPE_SNAIL
else if(holder.has_reagent(/datum/reagent/water))
- blood_type = "H2O"
+ blood_type = BLOOD_TYPE_H2O
else if(holder.has_reagent(/datum/reagent/toxin/slimejelly))
- blood_type = "TOX"
+ blood_type = BLOOD_TYPE_TOX
else
blood_type = null
return ..()
@@ -43,36 +44,36 @@
/obj/item/reagent_containers/blood/random/Initialize(mapload, vol)
icon_state = "bloodpack"
- blood_type = pick("A+", "A-", "B+", "B-", "O+", "O-", "L")
+ blood_type = pick(BLOOD_TYPE_A_PLUS, BLOOD_TYPE_A_MINUS, BLOOD_TYPE_B_PLUS, BLOOD_TYPE_B_MINUS, BLOOD_TYPE_O_PLUS, BLOOD_TYPE_O_MINUS, BLOOD_TYPE_LIZARD)
return ..()
/obj/item/reagent_containers/blood/a_plus
- blood_type = "A+"
+ blood_type = BLOOD_TYPE_A_PLUS
/obj/item/reagent_containers/blood/a_minus
- blood_type = "A-"
+ blood_type = BLOOD_TYPE_A_MINUS
/obj/item/reagent_containers/blood/b_plus
- blood_type = "B+"
+ blood_type = BLOOD_TYPE_B_PLUS
/obj/item/reagent_containers/blood/b_minus
- blood_type = "B-"
+ blood_type = BLOOD_TYPE_B_MINUS
/obj/item/reagent_containers/blood/o_plus
- blood_type = "O+"
+ blood_type = BLOOD_TYPE_O_PLUS
/obj/item/reagent_containers/blood/o_minus
- blood_type = "O-"
+ blood_type = BLOOD_TYPE_O_MINUS
/obj/item/reagent_containers/blood/lizard
- blood_type = "L"
+ blood_type = BLOOD_TYPE_LIZARD
/obj/item/reagent_containers/blood/ethereal
- blood_type = "LE"
+ blood_type = BLOOD_TYPE_ETHEREAL
unique_blood = /datum/reagent/consumable/liquidelectricity
/obj/item/reagent_containers/blood/snail
- blood_type = "S"
+ blood_type = BLOOD_TYPE_SNAIL
unique_blood = /datum/reagent/lube
/obj/item/reagent_containers/blood/snail/examine()
@@ -80,7 +81,7 @@
. += span_notice("It's a bit slimy... The label indicates that this is meant for snails.")
/obj/item/reagent_containers/blood/podperson
- blood_type = "H2O"
+ blood_type = BLOOD_TYPE_H2O
unique_blood = /datum/reagent/water
/obj/item/reagent_containers/blood/podperson/examine()
@@ -89,7 +90,7 @@
// for slimepeople
/obj/item/reagent_containers/blood/toxin
- blood_type = "TOX"
+ blood_type = BLOOD_TYPE_TOX
unique_blood = /datum/reagent/toxin/slimejelly
/obj/item/reagent_containers/blood/toxin/examine()
@@ -97,7 +98,7 @@
. += span_notice("There is a toxin warning on the label. This is for slimepeople.")
/obj/item/reagent_containers/blood/universal
- blood_type = "U"
+ blood_type = BLOOD_TYPE_UNIVERSAL
/obj/item/reagent_containers/blood/attackby(obj/item/tool, mob/user, list/modifiers)
if (IS_WRITING_UTENSIL(tool))
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
index 2a6f863f725..8dd8c3bf040 100644
--- a/code/modules/religion/burdened/psyker.dm
+++ b/code/modules/religion/burdened/psyker.dm
@@ -65,7 +65,7 @@
return
apply_damage(50, BRUTE, BODY_ZONE_HEAD)
to_chat(src, span_userdanger("Your head splits open! Your brain mutates!"))
- new /obj/effect/gibspawner/generic(drop_location(), src)
+ new /obj/effect/gibspawner/generic(drop_location(), src, get_blood_dna_list())
emote("scream")
/// Proc with no side effects that turns someone into a psyker. returns FALSE if it could not psykerize.
diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm
index aa5de910cf5..41882eb356c 100644
--- a/code/modules/religion/religion_sects.dm
+++ b/code/modules/religion/religion_sects.dm
@@ -384,10 +384,10 @@
if(!HAS_TRAIT(chaplain, TRAIT_NOBLOOD))
if(target.blood_volume < BLOOD_VOLUME_SAFE)
- var/target_blood_data = target.get_blood_data(target.get_blood_id())
- var/chaplain_blood_data = chaplain.get_blood_data(chaplain.get_blood_id())
+ var/datum/blood_type/target_blood_data = target.dna.blood_type
+ var/datum/blood_type/chaplain_blood_data = chaplain.dna.blood_type
var/transferred_blood_amount = min(chaplain.blood_volume, BLOOD_VOLUME_SAFE - target.blood_volume)
- if(transferred_blood_amount && (chaplain_blood_data["blood_type"] in get_safe_blood(target_blood_data["blood_type"])))
+ if(transferred_blood_amount && (chaplain_blood_data.type_key() in target_blood_data.compatible_types))
transferred = TRUE
chaplain.transfer_blood_to(target, transferred_blood_amount, forced = TRUE)
if(target.blood_volume > BLOOD_VOLUME_EXCESS)
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 66df0d4680d..047844fe78d 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -41,7 +41,7 @@
///Defines when a bodypart should not be changed. Example: BP_BLOCK_CHANGE_SPECIES prevents the limb from being overwritten on species gain
var/change_exempt_flags = NONE
///Random flags that describe this bodypart
- var/bodypart_flags = NONE
+ var/bodypart_flags = BODYPART_VIRGIN
///Whether the bodypart (and the owner) is husked.
var/is_husked = FALSE
@@ -213,6 +213,8 @@
/// If we even wanna try and handle icons/overlays of the limb (Taurs don't, f.e.). See update_body_parts
var/show_icon = TRUE
// SKYRAT EDIT END
+ /// The cached info about the blood this organ belongs to, set during on_removal()
+ var/list/blood_dna_info
/obj/item/bodypart/apply_fantasy_bonuses(bonus)
. = ..()
@@ -244,6 +246,8 @@
if(!IS_ORGANIC_LIMB(src))
grind_results = null
+ else
+ blood_dna_info = list("UNKNOWN DNA" = get_blood_type(BLOOD_TYPE_O_PLUS))
name = "[limb_id] [parse_zone(body_zone)]"
update_icon_dropped()
@@ -962,6 +966,14 @@
SHOULD_CALL_PARENT(TRUE)
if(IS_ORGANIC_LIMB(src))
+ // Try to add a cached blood type data, we must do it in here because for some reason DNA gets initialized AFTER the mob's limbs are created.
+ // Should be fine as this gets called before all the important stuff happens
+ if(!(bodypart_flags & ORGAN_VIRGIN) && owner?.dna?.blood_type)
+ blood_dna_info = owner.get_blood_dna_list()
+ // need to remove the synethic blood DNA that is initialized
+ // wash also adds the blood dna again
+ wash(CLEAN_TYPE_BLOOD)
+ bodypart_flags &= ~BODYPART_VIRGIN
if(!(bodypart_flags & BODYPART_UNHUSKABLE) && owner && HAS_TRAIT(owner, TRAIT_HUSK))
dmg_overlay_type = "" //no damage overlay shown when husked
is_husked = TRUE
@@ -1044,12 +1056,19 @@
SIGNAL_HANDLER
wash(clean_types)
+/obj/item/bodypart/wash(clean_types)
+ . = ..()
+
+ // always add the original dna to the organ after it's washed
+ if(IS_ORGANIC_LIMB(src) && (clean_types & CLEAN_TYPE_BLOOD))
+ add_blood_DNA(blood_dna_info)
+
/// To update the bodypart's icon when not attached to a mob
/obj/item/bodypart/proc/update_icon_dropped()
SHOULD_CALL_PARENT(TRUE)
cut_overlays()
- var/list/standing = get_limb_icon(TRUE)
+ var/list/standing = get_limb_icon(dropped = TRUE)
if(!standing.len)
icon_state = initial(icon_state)//no overlays found, we default back to initial icon.
return
@@ -1081,7 +1100,7 @@
update_icon_dropped()
///Generates an /image for the limb to be used as an overlay
-/obj/item/bodypart/proc/get_limb_icon(dropped)
+/obj/item/bodypart/proc/get_limb_icon(dropped, mob/living/carbon/update_on)
SHOULD_CALL_PARENT(TRUE)
RETURN_TYPE(/list)
@@ -1091,7 +1110,13 @@
if(dropped && dmg_overlay_type)
if(brutestate)
- . += image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER)
+ // divided into two overlays: one that gets colored and one that doesn't.
+ var/image/brute_blood_overlay = image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER)
+ brute_blood_overlay.color = get_blood_dna_color(update_on ? update_on.get_blood_dna_list() : blood_dna_info) // living mobs can just get it fresh, dropped limbs use blood_dna_info
+ var/mutable_appearance/brute_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0_overlay", -DAMAGE_LAYER, appearance_flags = RESET_COLOR)
+ if(brute_damage_overlay)
+ brute_blood_overlay.overlays += brute_damage_overlay
+ . += brute_blood_overlay
if(burnstate)
. += image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER)
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 34595198354..2a0b1a4ec91 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -24,6 +24,9 @@
if (wounding_type)
LAZYSET(limb_owner.body_zone_dismembered_by, body_zone, wounding_type)
+ if (can_bleed())
+ limb_owner.bleed(rand(20, 40))
+
drop_limb(dismembered = TRUE)
limb_owner.update_equipment_speed_mods() // Update in case speed affecting item unequipped by dismemberment
@@ -37,8 +40,8 @@
burn()
return TRUE
if (can_bleed())
- add_mob_blood(limb_owner)
limb_owner.bleed(rand(20, 40))
+
var/direction = pick(GLOB.cardinals)
var/t_range = rand(2,max(throw_range/2, 2))
var/turf/target_turf = get_turf(src)
@@ -98,6 +101,7 @@
SEND_SIGNAL(owner, COMSIG_CARBON_REMOVE_LIMB, src, special, dismembered)
SEND_SIGNAL(src, COMSIG_BODYPART_REMOVED, owner, special, dismembered)
bodypart_flags &= ~BODYPART_IMPLANTED //limb is out and about, it can't really be considered an implant
+ add_mob_blood(owner)
owner.remove_bodypart(src, special)
for(var/datum/scar/scar as anything in scars)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 711138bf8f1..d49f3f4dea3 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -170,7 +170,7 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/obj/item/bodypart/head/get_limb_icon(dropped)
+/obj/item/bodypart/head/get_limb_icon(dropped, mob/living/carbon/update_on)
. = ..()
. += get_hair_and_lips_icon(dropped)
diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm
index 28efa5e4e36..ba3a351e0c5 100644
--- a/code/modules/surgery/organs/_organ.dm
+++ b/code/modules/surgery/organs/_organ.dm
@@ -8,7 +8,7 @@
/// Reference to the limb we're inside of
var/obj/item/bodypart/bodypart_owner
/// The cached info about the blood this organ belongs to
- var/list/blood_dna_info = list("Synthetic DNA" = "O+") // not every organ spawns inside a person
+ var/list/blood_dna_info // not every organ spawns inside a person
/// The body zone this organ is supposed to inhabit.
var/zone = BODY_ZONE_CHEST
/**
@@ -77,6 +77,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/obj/item/organ/Initialize(mapload)
. = ..()
+ blood_dna_info = list("UNKNOWN DNA" = get_blood_type(BLOOD_TYPE_O_PLUS))
if(organ_flags & ORGAN_EDIBLE)
AddComponentFrom(
SOURCE_EDIBLE_INNATE, \
diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm
index 47edbea1b69..2f5127e3131 100644
--- a/code/modules/surgery/organs/organ_movement.dm
+++ b/code/modules/surgery/organs/organ_movement.dm
@@ -233,12 +233,12 @@
return TRUE
-/// Called on limb removal to remove limb specific limb effects or statusses
+// Called on limb removal to remove limb specific limb effects or statuses
/obj/item/organ/proc/on_bodypart_remove(obj/item/bodypart/limb, movement_flags)
SHOULD_CALL_PARENT(TRUE)
if(!IS_ROBOTIC_ORGAN(src) && !(item_flags & NO_BLOOD_ON_ITEM) && !QDELING(src))
- AddElement(/datum/element/decal/blood)
+ AddElement(/datum/element/decal/blood, _color = get_blood_dna_color(blood_dna_info))
item_flags &= ~ABSTRACT
REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
@@ -281,7 +281,7 @@
/obj/item/organ/proc/on_surgical_removal(mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, old_owner, target_zone, tool)
- RemoveElement(/datum/element/decal/blood)
+ RemoveElement(/datum/element/decal/blood, _color = old_owner.dna.blood_type.get_color())
/**
* Proc that gets called when the organ is surgically inserted by someone. Seem familiar?
*/
diff --git a/code/modules/surgery/surgery_step.dm b/code/modules/surgery/surgery_step.dm
index aa5dab1e97b..38285e8ec28 100644
--- a/code/modules/surgery/surgery_step.dm
+++ b/code/modules/surgery/surgery_step.dm
@@ -240,6 +240,11 @@
span_notice("[user] succeeds!"),
span_notice("[user] finishes."),
)
+ if(ishuman(user))
+ var/mob/living/carbon/human/surgeon = user
+ surgeon.add_blood_DNA_to_items(target.get_blood_dna_list(), ITEM_SLOT_GLOVES)
+ else
+ user.add_mob_blood(target)
return TRUE
/datum/surgery_step/proc/play_success_sound(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/unit_tests/quirks.dm b/code/modules/unit_tests/quirks.dm
index 8816d6aec53..4a036ab8169 100644
--- a/code/modules/unit_tests/quirks.dm
+++ b/code/modules/unit_tests/quirks.dm
@@ -45,11 +45,11 @@
/// Ensures the blood deficiency quirk updates its mail goodies correctly
/datum/unit_test/blood_deficiency_mail
var/list/species_to_test = list(
+ /datum/species/human = /obj/item/reagent_containers/blood/o_minus,
/datum/species/lizard = /obj/item/reagent_containers/blood/lizard,
/datum/species/ethereal = /obj/item/reagent_containers/blood/ethereal,
/datum/species/skeleton = null, // Anyone with noblood should not get a blood bag
/datum/species/jelly = /obj/item/reagent_containers/blood/toxin,
- /datum/species/human = /obj/item/reagent_containers/blood/o_minus,
)
/datum/unit_test/blood_deficiency_mail/Run()
@@ -57,10 +57,14 @@
dummy.add_quirk(/datum/quirk/blooddeficiency)
var/datum/quirk/blooddeficiency/quirk = dummy.get_quirk(/datum/quirk/blooddeficiency)
- TEST_ASSERT((species_to_test[dummy.dna.species.type] in quirk.mail_goodies), "Blood deficiency quirk spawned with no mail goodies!")
+ TEST_ASSERT((species_to_test[dummy.dna.species.type] in quirk.mail_goodies), "Blood deficiency quirk did not get the right blood bag in its mail goodies for [dummy.dna.species.type]! \
+ It should be getting species_to_test[dummy.dna.species.type]." \
+ )
for(var/species_type in species_to_test)
var/last_species = dummy.dna.species.type
+ if(species_type == /datum/species/human) // we already tested this above, and setting species again will cause it to randomize
+ continue
dummy.set_species(species_type)
// Test that the new species has the correct blood bag
if(!isnull(species_to_test[species_type]))
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
index d4378ea7e83..a0828bf8a55 100644
--- a/code/modules/vehicles/cars/clowncar.dm
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -134,7 +134,10 @@
carbon_occupant.set_eye_blur_if_lower(rand(10 SECONDS, 20 SECONDS))
hittarget_living.adjustBruteLoss(200)
- new /obj/effect/decal/cleanable/blood/splatter(get_turf(hittarget_living))
+ var/obj/effect/decal/cleanable/blood/splatter/blood_splatter = new /obj/effect/decal/cleanable/blood/splatter(get_turf(hittarget_living))
+ if(iscarbon(hittarget_living))
+ var/mob/living/carbon/carbon_target = hittarget_living
+ blood_splatter.add_mob_blood(carbon_target)
log_combat(src, hittarget_living, "rammed into", null, "injuring all passengers and killing the [hittarget_living]")
dump_mobs(TRUE)
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 91e6e44a2d3..70ecf091c07 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -1053,7 +1053,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
carbon_target.visible_message(span_danger("[carbon_head] explodes in a shower of gore beneath [src]!"), span_userdanger("Oh f-"))
carbon_head.drop_organs()
qdel(carbon_head)
- new /obj/effect/gibspawner/human/bodypartless(get_turf(carbon_target))
+ new /obj/effect/gibspawner/human/bodypartless(get_turf(carbon_target), carbon_target)
return TRUE
return FALSE
diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi
index 2ae6d40fc33..ebf768329f9 100644
Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ
diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi
index fb31da10ee9..420737cf34c 100644
Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ
diff --git a/icons/effects/footprints.dmi b/icons/effects/footprints.dmi
index a00c6a0f780..cef2898e893 100644
Binary files a/icons/effects/footprints.dmi and b/icons/effects/footprints.dmi differ
diff --git a/icons/mob/effects/bleed_overlays.dmi b/icons/mob/effects/bleed_overlays.dmi
index 68c167e11ef..f376eb05f03 100644
Binary files a/icons/mob/effects/bleed_overlays.dmi and b/icons/mob/effects/bleed_overlays.dmi differ
diff --git a/icons/mob/effects/dam_mob.dmi b/icons/mob/effects/dam_mob.dmi
index 6390a1b34d6..cc719551bc7 100644
Binary files a/icons/mob/effects/dam_mob.dmi and b/icons/mob/effects/dam_mob.dmi differ
diff --git a/icons/obj/machines/kitchen.dmi b/icons/obj/machines/kitchen.dmi
index 1bd254942b2..3c20847256f 100644
Binary files a/icons/obj/machines/kitchen.dmi and b/icons/obj/machines/kitchen.dmi differ
diff --git a/icons/obj/machines/recycling.dmi b/icons/obj/machines/recycling.dmi
index 9a6dffaaee2..770ee59b139 100644
Binary files a/icons/obj/machines/recycling.dmi and b/icons/obj/machines/recycling.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index f9c3a7f7dbf..f8ee9954f12 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3146,6 +3146,7 @@
#include "code\modules\admin\smites\boneless.dm"
#include "code\modules\admin\smites\brain_damage.dm"
#include "code\modules\admin\smites\bsa.dm"
+#include "code\modules\admin\smites\cluwndice.dm"
#include "code\modules\admin\smites\curse_of_babel.dm"
#include "code\modules\admin\smites\dock_pay.dm"
#include "code\modules\admin\smites\dust.dm"
@@ -5042,6 +5043,7 @@
#include "code\modules\mob\eye\camera\camera.dm"
#include "code\modules\mob\eye\camera\remote.dm"
#include "code\modules\mob\living\blood.dm"
+#include "code\modules\mob\living\blood_types.dm"
#include "code\modules\mob\living\damage_procs.dm"
#include "code\modules\mob\living\death.dm"
#include "code\modules\mob\living\emote.dm"