There will be (colorful) blood: datumizes bloodtypes, greyscales blood sprites, and fixes a lot of inconsistencies with gibs and forensic data (#90593)
## About The Pull Request This PR: - Converts all of the blood types into their own datums, which can be set up to have their own colors, descriptions, and other fun unique properties. For example, the clown blood that is constantly randomizing itself. - Converts all the blood decals into greyscale, which in turn eliminates the need for separate xeno sprites. They both use the same ones now. - Audit of blood splatters/gibs/bodyparts/organs to make sure that they are getting the correct forensic data applied to them. - For the admins: Adds a clown blood smite. My primary goal with was to make the appearance of the new sprites look almost indistinguishable to the original ones. I consider this a "first pass", as in there are still some further refactors I would like to do on the backend side, but am satisfied with it enough to push it forward as a first step towards a better blood system! I didn't want to do too much at once because of A) fatigue and B) easier to test things to make sure I'm not breaking something important this way. This has been test-merged on Nova for over a week now and has been going great, so I finally got around to upstreaming the bones to TG. Although I did test it a bit you may want to TM it just in case I missed some things when copying it over.
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -113,6 +113,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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -55,7 +55,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
|
||||
@@ -109,7 +110,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
|
||||
@@ -428,7 +429,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
|
||||
|
||||
@@ -473,7 +474,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
|
||||
@@ -588,7 +589,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,\
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -118,7 +118,7 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
|
||||
|
||||
var/datum/record/locked/lockfile = new(
|
||||
age = person.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),
|
||||
@@ -135,7 +135,7 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
|
||||
|
||||
new /datum/record/crew(
|
||||
age = person.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),
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -1750,7 +1750,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
|
||||
@@ -1768,7 +1768,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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
return FALSE
|
||||
|
||||
target.age = 18
|
||||
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 = ""
|
||||
|
||||
@@ -184,6 +184,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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
name = "xeno blood"
|
||||
desc = "It's green and acidic. It looks like... <i>blood?</i>"
|
||||
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)))
|
||||
|
||||
@@ -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)
|
||||
|
||||
/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] <B>[initial(item.name)]</B>."
|
||||
for(var/species in species_types)
|
||||
// god help me
|
||||
if(species == "unknown")
|
||||
. += "Some <B>feet</B>."
|
||||
else if(species == SPECIES_MONKEY)
|
||||
. += "[icon2html('icons/mob/human/human.dmi', user, "monkey")] Some <B>monkey paws</B>."
|
||||
else if(species == SPECIES_HUMAN)
|
||||
. += "[icon2html('icons/mob/human/bodyparts.dmi', user, "default_human_l_leg")] Some <B>human feet</B>."
|
||||
else
|
||||
. += "[icon2html('icons/mob/human/bodyparts.dmi', user, "[species]_l_leg")] Some <B>[species] feet</B>."
|
||||
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] <B>[initial(item.name)]</B>."
|
||||
|
||||
for(var/species in species_types)
|
||||
// god help me
|
||||
if(species == "unknown")
|
||||
. += "Some <B>feet</B>."
|
||||
else if(species == SPECIES_MONKEY)
|
||||
. += "[icon2html('icons/mob/human/human.dmi', user, "monkey")] Some <B>monkey paws</B>."
|
||||
else if(species == SPECIES_HUMAN)
|
||||
. += "[icon2html('icons/mob/human/bodyparts.dmi', user, "default_human_l_leg")] Some <B>human feet</B>."
|
||||
else
|
||||
. += "[icon2html('icons/mob/human/bodyparts.dmi', user, "[species]_l_leg")] Some <B>[species] feet</B>."
|
||||
|
||||
/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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]<br>"
|
||||
var/datum/blood_type/blood_type = scanned.dna.blood_type
|
||||
autopsy_information += "Blood Type: [blood_type.name]<br>"
|
||||
autopsy_information += "Blood Volume: [scanned.blood_volume] cl ([blood_percent]%) <br>"
|
||||
|
||||
for(var/datum/disease/diseases as anything in scanned.diseases)
|
||||
|
||||
@@ -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 += "<span class='alert ml-1'>Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl,</span> [span_info("type: [blood_type]")]<br>"
|
||||
render_list += "<span class='alert ml-1'>Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl,</span> [span_info("type: [blood_type.name]")]<br>"
|
||||
render_list += "<span class='alert ml-1'>Recommendation: [blood_type.restoration_chem::name] supplements or blood transfusion.</span><br>"
|
||||
else if(carbontarget.blood_volume <= BLOOD_VOLUME_OKAY)
|
||||
render_list += "<span class='alert ml-1'>Blood level: <b>CRITICAL [blood_percent]%</b>, [carbontarget.blood_volume] cl,</span> [span_info("type: [blood_type]")]<br>"
|
||||
render_list += "<span class='alert ml-1'>Recommendation: [blood_type.restoration_chem::name] supplements or blood transfusion.</span><br>"
|
||||
else
|
||||
render_list += "<span class='info ml-1'>Blood level: [blood_percent]%, [carbontarget.blood_volume] cl, type: [blood_type]</span><br>"
|
||||
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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,\
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
// Snowflake for Ethereals
|
||||
human.updatehealth()
|
||||
human.updateappearance(mutcolor_update = TRUE)
|
||||
@@ -55,7 +55,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)
|
||||
|
||||
14
code/modules/admin/smites/cluwndice.dm
Normal file
@@ -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')
|
||||
@@ -36,7 +36,7 @@
|
||||
for(var/entry in GLOB.human_list)
|
||||
var/mob/living/carbon/human/subject = entry
|
||||
if(subject.ckey)
|
||||
data += "<tr><td>[subject]</td><td>[subject.dna.unique_enzymes]</td><td>[subject.dna.blood_type]</td></tr>"
|
||||
data += "<tr><td>[subject]</td><td>[subject.dna.unique_enzymes]</td><td>[subject.dna.blood_type.name]</td></tr>"
|
||||
data += "</table>"
|
||||
|
||||
var/datum/browser/browser = new(usr, "DNA", "DNA Log", 440, 410)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -143,11 +143,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,\
|
||||
)
|
||||
|
||||
|
||||
@@ -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,\
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -64,9 +64,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
|
||||
|
||||
/obj/item/clothing/shoes/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -41,7 +41,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)
|
||||
..()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -203,7 +203,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
|
||||
|
||||
@@ -277,7 +277,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,\
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,\
|
||||
)
|
||||
|
||||
|
||||
@@ -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)))
|
||||
@@ -371,14 +364,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.
|
||||
@@ -388,23 +381,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
|
||||
|
||||
247
code/modules/mob/living/blood_types.dm
Normal file
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -1454,12 +1454,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
|
||||
|
||||
@@ -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)
|
||||
if(on_fire)
|
||||
to_chat(helper, span_warning("You can't put [p_them()] out with just your bare hands!"))
|
||||
@@ -315,6 +332,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."))
|
||||
|
||||
@@ -351,6 +369,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)
|
||||
|
||||
@@ -718,4 +737,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
|
||||
|
||||
@@ -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))
|
||||
@@ -496,7 +505,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
|
||||
|
||||
@@ -381,20 +381,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 = 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)
|
||||
|
||||
@@ -760,10 +764,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)
|
||||
|
||||
@@ -120,7 +120,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
target.dna.features["tail_monkey"] = get_consistent_feature_entry(SSaccessories.tails_list_monkey)
|
||||
target.dna.features["pod_hair"] = get_consistent_feature_entry(SSaccessories.pod_hair_list)
|
||||
target.dna.features["caps"] = get_consistent_feature_entry(SSaccessories.caps_list)
|
||||
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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -182,9 +182,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
|
||||
@@ -908,7 +912,7 @@ generate/load female uniform sprites matching all previously decided variables
|
||||
|
||||
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()
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -16,6 +16,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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -524,7 +524,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
|
||||
|
||||
@@ -428,6 +428,7 @@
|
||||
M.visible_message(span_warning("[src] grabs [M] [grabbed_by_hands ? "by their hands":"passively"]!"), \
|
||||
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"]!"))
|
||||
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)
|
||||
@@ -1100,7 +1101,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
|
||||
@@ -1112,14 +1113,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
|
||||
..()
|
||||
|
||||
@@ -3052,3 +3053,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
|
||||
|
||||
@@ -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
|
||||
exposed_carbon.blood_volume = min(exposed_carbon.blood_volume + round(reac_volume, 0.1), BLOOD_VOLUME_MAXIMUM)
|
||||
@@ -72,12 +75,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)
|
||||
@@ -152,7 +162,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))
|
||||
@@ -1216,11 +1226,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."
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -209,6 +209,8 @@
|
||||
var/datum/bodypart_overlay/texture/texture_bodypart_overlay
|
||||
/// Lazylist of /datum/status_effect/grouped/bodypart_effect types. Instances of this are applied to the carbon when added the limb is attached, and merged with similair limbs
|
||||
var/list/bodypart_effects
|
||||
/// 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)
|
||||
. = ..()
|
||||
@@ -240,6 +242,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()
|
||||
@@ -939,6 +943,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
|
||||
@@ -1007,12 +1019,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
|
||||
@@ -1044,7 +1063,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)
|
||||
|
||||
@@ -1054,7 +1073,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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -90,6 +93,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, \
|
||||
|
||||
@@ -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)
|
||||
@@ -280,7 +280,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?
|
||||
*/
|
||||
|
||||
@@ -224,6 +224,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)
|
||||
|
||||
@@ -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]))
|
||||
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -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)
|
||||
|
||||
@@ -1050,7 +1050,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
|
||||
|
||||
|
Before Width: | Height: | Size: 397 KiB After Width: | Height: | Size: 388 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 43 KiB |