[PORT]: Elementizes and Greyscales blood decals/overlays 2 (#21126)

cause cowbot said it's good to go
---------

Co-authored-by: cowbot92 <75333826+cowbot92@users.noreply.github.com>
This commit is contained in:
Molti
2024-01-03 14:39:19 -06:00
committed by GitHub
parent 1a6f9ea79e
commit c07b40010f
70 changed files with 898 additions and 331 deletions

View File

@@ -330,6 +330,9 @@
#define SOFA_BROWN "#a75400"
#define SOFA_MAROON "#830000"
/// Color used for default blood
#define COLOR_BLOOD "#CC0000"
GLOBAL_LIST_INIT(cable_colors, list(
CABLE_COLOR_BLUE = CABLE_HEX_COLOR_BLUE,
CABLE_COLOR_CYAN = CABLE_HEX_COLOR_CYAN,

View File

@@ -14,6 +14,11 @@
/// You do not need this if you are only unregistering signals, for instance.
/// You would need it if you are doing something like removing the target from a processing list.
#define ELEMENT_DETACH_ON_HOST_DESTROY (1 << 0)
// /datum/element flags
/// Causes the detach proc to be called when the host object is being deleted
#define ELEMENT_DETACH (1 << 0)
/**
* Only elements created with the same arguments given after `argument_hash_start_idx` share an element instance
* The arguments are the same when the text and number values are the same and all other values have the same ref

View File

@@ -10,6 +10,10 @@
/// Every proc you pass to RegisterSignal must have this.
#define SIGNAL_HANDLER SHOULD_NOT_SLEEP(TRUE)
/// Signifies that this proc is used to handle signals, but also sleeps.
/// Do not use this for new work.
#define SIGNAL_HANDLER_DOES_SLEEP
/// A wrapper for _AddElement that allows us to pretend we're using normal named arguments
#define AddElement(arguments...) _AddElement(list(##arguments))

View File

@@ -115,12 +115,11 @@
GLOBAL_LIST_EMPTY(bloody_footprints_cache)
//Bloody shoes/footprints
#define MAX_SHOE_BLOODINESS 100
#define BLOODY_FOOTPRINT_BASE_ALPHA 150
#define BLOOD_GAIN_PER_STEP 100
#define BLOOD_LOSS_PER_STEP 5
#define BLOOD_LOSS_IN_SPREAD 20
#define BLOOD_AMOUNT_PER_DECAL 20
#define BLOODY_FOOTPRINT_BASE_ALPHA 80 /// Minimum alpha of footprints
#define BLOOD_AMOUNT_PER_DECAL 50 /// How much blood a regular blood splatter contains
#define BLOOD_ITEM_MAX 200 /// How much blood an item can have stuck on it
#define BLOOD_POOL_MAX 300 /// How much blood a blood decal can contain
#define BLOOD_FOOTPRINTS_MIN 5 /// How much blood a footprint need to at least contain
//Bloody shoe blood states
#define BLOOD_STATE_HUMAN "blood"

View File

@@ -737,3 +737,15 @@
///sort any value in a list
/proc/sort_list(list/list_to_sort, cmp=/proc/cmp_text_asc)
return sortTim(list_to_sort.Copy(), cmp)
/// ORs two lazylists together without inserting errant nulls, returning a new list and not modifying the existing lists.
#define LAZY_LISTS_OR(left_list, right_list)\
(length(left_list)\
? length(right_list)\
? (left_list | right_list)\
: left_list.Copy()\
: length(right_list)\
? right_list.Copy()\
: null\
)

View File

@@ -1,6 +1,3 @@
/proc/random_blood_type()
return pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
/proc/random_eye_color()
switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino"))
if("brown")

View File

@@ -39,6 +39,14 @@ GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup)
GLOBAL_LIST_EMPTY(emote_list)
GLOBAL_LIST_INIT(blood_types, generate_blood_types())
/proc/generate_blood_types()
. = list()
for(var/path in subtypesof(/datum/blood_type))
var/datum/blood_type/new_type = new path()
.[new_type.name] = new_type
/// Keys are the names of the accents, values are the name of their .json file.
GLOBAL_LIST_INIT(accents_name2file, strings("accents.json", "accent_file_names", directory = "strings/accents"))
/// List of all accents

View File

@@ -0,0 +1,91 @@
/datum/blood_type
/// Displayed name of the blood type.
var/name = "?"
/// Shown color of the blood type.
var/color = COLOR_BLOOD
/// Blood types that are safe to use with people that have this blood type.
var/compatible_types = list()
/datum/blood_type/New()
. = ..()
compatible_types |= /datum/blood_type/universal
/datum/blood_type/universal
name = "U"
/datum/blood_type/universal/New()
. = ..()
compatible_types |= subtypesof(/datum/blood_type)
////////////////////////////////////////////////////////////////
//--------------------Normal human bloodtypes-----------------//
////////////////////////////////////////////////////////////////
/datum/blood_type/a_minus
name = "A-"
compatible_types = list(/datum/blood_type/a_minus, /datum/blood_type/o_minus)
/datum/blood_type/a_plus
name = "A+"
compatible_types = list(/datum/blood_type/a_minus, /datum/blood_type/a_plus, /datum/blood_type/o_minus, /datum/blood_type/o_plus)
/datum/blood_type/b_minus
name = "B-"
compatible_types = list(/datum/blood_type/b_minus, /datum/blood_type/o_minus, /datum/blood_type/universal)
/datum/blood_type/b_plus
name = "B+"
compatible_types = list(/datum/blood_type/b_minus, /datum/blood_type/b_plus, /datum/blood_type/o_minus, /datum/blood_type/o_plus)
/datum/blood_type/ab_minus
name = "AB-"
compatible_types = list(/datum/blood_type/b_minus, /datum/blood_type/a_minus, /datum/blood_type/ab_minus, /datum/blood_type/o_minus)
/datum/blood_type/ab_plus
name = "AB+"
compatible_types = list(/datum/blood_type/b_minus, /datum/blood_type/a_minus, /datum/blood_type/ab_minus, /datum/blood_type/o_minus)
/datum/blood_type/o_minus
name = "O-"
compatible_types = list(/datum/blood_type/o_minus)
/datum/blood_type/o_plus
name = "O+"
compatible_types = list(/datum/blood_type/o_minus, /datum/blood_type/o_plus)
////////////////////////////////////////////////////////////////
//--------------------Other species bloodtypes----------------//
////////////////////////////////////////////////////////////////
/datum/blood_type/lizard
name = "L"
color = LIGHT_COLOR_BLUEGREEN
compatible_types = list(/datum/blood_type/lizard)
/datum/blood_type/universal/synthetic //Blood for preterni
name = "Synthetic"
color = LIGHT_COLOR_ELECTRIC_CYAN
/*
The species have exotic blood, but with how dna is stored, they still need a blood type
They're literally ONLY used to colour bloodsplats as far as I know (maybe it will be possible to podclone from bloodsplats)
*/
/datum/blood_type/xenomorph //for xenomorph gib dna and polysmorph bloodsplats
name = "X"
color = "#00FF32"
compatible_types = list(/datum/blood_type/xenomorph)
/datum/blood_type/electricity
name = "E"
color = "#cbee63" //slightly more yellowy than regular liquid electricity because of the grey scale image used
compatible_types = list(/datum/blood_type/electricity)
////////////////////////////////////////////////////////////////
//-----------------Wonky simplemob(?) bloodtypes--------------//
////////////////////////////////////////////////////////////////
/datum/blood_type/animal //for simplemob gib dna
name = "Y-"
compatible_types = list(/datum/blood_type/animal)
/datum/blood_type/gorilla
name = "G"
compatible_types = list(/datum/blood_type/gorilla)

View File

@@ -0,0 +1,289 @@
//Component for clothing items that can pick up blood from decals and spread it around everywhere when walking, such as shoes or suits with integrated shoes.
/datum/component/bloodysoles
/// The type of the last grub pool we stepped in, used to decide the type of footprints to make
var/last_blood_state = BLOOD_STATE_NOT_BLOODY
/// How much of each grubby type we have on our feet
var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
/// The ITEM_SLOT_* slot the item is equipped on, if it is.
var/equipped_slot
/// The parent item but casted into atom type for easier use.
var/atom/parent_atom
/// Either the mob carrying the item, or the mob itself for the /feet component subtype
var/mob/living/carbon/wielder
/// The world.time when we last picked up blood
var/last_pickup
/datum/component/bloodysoles/Initialize()
if(!isclothing(parent))
return COMPONENT_INCOMPATIBLE
parent_atom = parent
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
//Unregisters from the wielder if necessary
/datum/component/bloodysoles/proc/unregister()
if(!QDELETED(wielder))
UnregisterSignal(wielder, COMSIG_MOVABLE_MOVED)
UnregisterSignal(wielder, COMSIG_STEP_ON_BLOOD)
wielder = null
equipped_slot = null
//Returns true if the parent item is obscured by something else that the wielder is wearing
/datum/component/bloodysoles/proc/is_obscured()
return equipped_slot in wielder.check_obscured_slots(TRUE)
//Run to update the icon of the parent
/datum/component/bloodysoles/proc/update_icon()
var/obj/item/parent_item = parent
parent_item.update_slot_icon()
//Run to equally share the blood between us and a decal
/datum/component/bloodysoles/proc/share_blood(obj/effect/decal/cleanable/pool)
last_blood_state = pool.blood_state
// Share the blood between our boots and the blood pool
var/total_bloodiness = pool.bloodiness + bloody_shoes[last_blood_state]
// We can however be limited by how much blood we can hold
var/new_our_bloodiness = min(BLOOD_ITEM_MAX, total_bloodiness / 2)
bloody_shoes[last_blood_state] = new_our_bloodiness
pool.bloodiness = total_bloodiness - new_our_bloodiness // Give the pool the remaining blood incase we were limited
parent_atom.add_blood_DNA(pool.return_blood_DNA())
update_icon()
//Find a blood decal on a turf that matches our last_blood_state
/datum/component/bloodysoles/proc/find_pool_by_blood_state(turf/turfLoc, typeFilter = null)
for(var/obj/effect/decal/cleanable/blood/pool in turfLoc)
if(pool.blood_state == last_blood_state && (!typeFilter || istype(pool, typeFilter)))
return pool
//Adds the parent type to the footprint's shoe_types var
/datum/component/bloodysoles/proc/add_parent_to_footprint(obj/effect/decal/cleanable/blood/footprints/FP)
FP.shoe_types |= parent.type
/*
Called when the parent item is equipped by someone
Used to register our wielder
*/
/datum/component/bloodysoles/proc/on_equip(datum/source, mob/equipper, slot)
SIGNAL_HANDLER
if(!iscarbon(equipper))
return
var/obj/item/parent_item = parent
if(!(parent_item.slot_flags & slot))
unregister()
return
equipped_slot = slot
wielder = equipper
RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(wielder, COMSIG_STEP_ON_BLOOD, PROC_REF(on_step_blood))
/*
Called when the parent item has been dropped
Used to deregister our wielder
*/
/datum/component/bloodysoles/proc/on_drop(datum/source, mob/dropper)
SIGNAL_HANDLER
unregister()
/*
Called when the wielder has moved
Used to make bloody footprints on the ground
*/
/datum/component/bloodysoles/proc/on_moved(datum/source, OldLoc, Dir, Forced)
SIGNAL_HANDLER
if(bloody_shoes[last_blood_state] == 0)
return
if(QDELETED(wielder) || is_obscured())
return
if(!(wielder.mobility_flags & MOBILITY_STAND) || !wielder.has_gravity(wielder.loc))
return
var/half_our_blood = bloody_shoes[last_blood_state] / 2
// Add footprints in old loc if we have enough cream
if(half_our_blood >= BLOOD_FOOTPRINTS_MIN)
var/turf/oldLocTurf = get_turf(OldLoc)
var/obj/effect/decal/cleanable/blood/footprints/oldLocFP = find_pool_by_blood_state(oldLocTurf, /obj/effect/decal/cleanable/blood/footprints)
if(oldLocFP)
// Footprints found in the tile we left, add us to it
add_parent_to_footprint(oldLocFP)
if (!(oldLocFP.exited_dirs & wielder.dir))
oldLocFP.exited_dirs |= wielder.dir
oldLocFP.update_icon()
else if(find_pool_by_blood_state(oldLocTurf))
// No footprints in the tile we left, but there was some other blood pool there. Add exit footprints on it
bloody_shoes[last_blood_state] -= half_our_blood
update_icon()
oldLocFP = new(oldLocTurf)
if(!QDELETED(oldLocFP)) ///prints merged
oldLocFP.blood_state = last_blood_state
oldLocFP.exited_dirs |= wielder.dir
add_parent_to_footprint(oldLocFP)
oldLocFP.bloodiness = half_our_blood
oldLocFP.add_blood_DNA(parent_atom.return_blood_DNA())
oldLocFP.update_icon()
half_our_blood = bloody_shoes[last_blood_state] / 2
// If we picked up the blood on this tick in on_step_blood, don't make footprints at the same place
if(last_pickup && last_pickup == world.time)
return
// Create new footprints
if(half_our_blood >= BLOOD_FOOTPRINTS_MIN)
bloody_shoes[last_blood_state] -= half_our_blood
update_icon()
var/obj/effect/decal/cleanable/blood/footprints/FP = new(get_turf(parent_atom))
if(!QDELETED(FP)) ///prints merged
FP.blood_state = last_blood_state
FP.entered_dirs |= wielder.dir
add_parent_to_footprint(FP)
FP.bloodiness = half_our_blood
FP.add_blood_DNA(parent_atom.return_blood_DNA())
FP.update_icon()
/*
Called when the wielder steps in a pool of blood
Used to make the parent item bloody
*/
/datum/component/bloodysoles/proc/on_step_blood(datum/source, obj/effect/decal/cleanable/pool)
SIGNAL_HANDLER
if(QDELETED(wielder) || is_obscured())
return
if(istype(pool, /obj/effect/decal/cleanable/blood/footprints) && pool.blood_state == last_blood_state)
// The pool we stepped in was actually footprints with the same type
var/obj/effect/decal/cleanable/blood/footprints/pool_FP = pool
add_parent_to_footprint(pool_FP)
if((bloody_shoes[last_blood_state] / 2) >= BLOOD_FOOTPRINTS_MIN && !(pool_FP.entered_dirs & wielder.dir))
// If our feet are bloody enough, add an entered dir
pool_FP.entered_dirs |= wielder.dir
pool_FP.update_icon()
share_blood(pool)
last_pickup = world.time
/*
Called when the parent item is being washed
*/
/datum/component/bloodysoles/proc/on_clean(datum/source, clean_types)
SIGNAL_HANDLER
if(!(clean_types & CLEAN_TYPE_BLOOD) || last_blood_state == BLOOD_STATE_NOT_BLOODY)
return
bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
last_blood_state = BLOOD_STATE_NOT_BLOODY
update_icon()
return TRUE
/*
Like its parent but can be applied to carbon mobs instead of clothing items
*/
/datum/component/bloodysoles/feet
var/static/mutable_appearance/bloody_feet
/datum/component/bloodysoles/feet/Initialize()
if(!iscarbon(parent))
return COMPONENT_INCOMPATIBLE
parent_atom = parent
wielder = parent
if(!bloody_feet)
bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(parent, COMSIG_STEP_ON_BLOOD, PROC_REF(on_step_blood))
RegisterSignal(parent, COMSIG_CARBON_UNEQUIP_SHOECOVER, PROC_REF(unequip_shoecover))
RegisterSignal(parent, COMSIG_CARBON_EQUIP_SHOECOVER, PROC_REF(equip_shoecover))
/datum/component/bloodysoles/feet/update_icon()
. = list()
if(ishuman(wielder))// Monkeys get no bloody feet :(
if(HAS_BLOOD_DNA(wielder))
bloody_feet.color = bloody_feet.color = get_blood_dna_color(wielder.return_blood_DNA())
. += bloody_feet
if(bloody_shoes[BLOOD_STATE_HUMAN] > 0 && !is_obscured())
wielder.remove_overlay(SHOES_LAYER)
wielder.overlays_standing[SHOES_LAYER] = bloody_feet
wielder.apply_overlay(SHOES_LAYER)
else
wielder.update_inv_shoes()
/datum/component/bloodysoles/feet/add_parent_to_footprint(obj/effect/decal/cleanable/blood/footprints/FP)
if(ismonkey(wielder))
FP.species_types |= "monkey"
return
if(!ishuman(wielder))
FP.species_types |= "unknown"
return
// Find any leg of our human and add that to the footprint, instead of the default which is to just add the human type
for(var/X in wielder.bodyparts)
var/obj/item/bodypart/affecting = X
if(affecting.body_part == LEG_RIGHT || affecting.body_part == LEG_LEFT)
if(!affecting.bodypart_disabled)
FP.species_types |= affecting.limb_id
break
/datum/component/bloodysoles/feet/is_obscured()
if(wielder.shoes)
return TRUE
return ITEM_SLOT_FEET in wielder.check_obscured_slots(TRUE)
/datum/component/bloodysoles/feet/on_moved(datum/source, OldLoc, Dir, Forced)
if(wielder.num_legs < 2)
return
..()
/datum/component/bloodysoles/feet/on_step_blood(datum/source, obj/effect/decal/cleanable/pool)
if(wielder.num_legs < 2)
return
..()
/datum/component/bloodysoles/feet/proc/unequip_shoecover(datum/source)
SIGNAL_HANDLER
update_icon()
/datum/component/bloodysoles/feet/proc/equip_shoecover(datum/source)
SIGNAL_HANDLER
update_icon()

View File

@@ -1,39 +0,0 @@
/datum/component/decal/blood
dupe_mode = COMPONENT_DUPE_UNIQUE
/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_TYPE_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
RegisterSignal(parent, COMSIG_ATOM_GET_EXAMINE_NAME, PROC_REF(get_examine_name))
/datum/component/decal/blood/generate_appearance(_icon, _icon_state, _dir, _layer, _color)
var/obj/item/I = parent
if(!_icon)
_icon = 'icons/effects/blood.dmi'
if(!_icon_state)
_icon_state = "itemblood"
var/icon = initial(I.icon)
var/icon_state = initial(I.icon_state)
if(!icon || !icon_state)
// It's something which takes on the look of other items, probably
icon = I.icon
icon_state = I.icon_state
var/static/list/blood_splatter_appearances = list()
//try to find a pre-processed blood-splatter. otherwise, make a new one
var/index = "[REF(icon)]-[icon_state]"
pic = blood_splatter_appearances[index]
if(!pic)
var/icon/blood_splatter_icon = icon(initial(I.icon), initial(I.icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object
blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent)
blood_splatter_icon.Blend(icon(_icon, _icon_state), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant
pic = mutable_appearance(blood_splatter_icon, initial(I.icon_state))
blood_splatter_appearances[index] = pic
return TRUE
/datum/component/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override)
var/atom/A = parent
override[EXAMINE_POSITION_ARTICLE] = A.gender == PLURAL? "some" : "a"
override[EXAMINE_POSITION_BEFORE] = " blood-stained "
return COMPONENT_EXNAME_CHANGED

View File

@@ -10,11 +10,11 @@
var/list/scents //assoc dna = carbon mob
/datum/component/forensics/InheritComponent(datum/component/forensics/F, original) //Use of | and |= being different here is INTENTIONAL.
fingerprints = fingerprints | F.fingerprints
hiddenprints = hiddenprints | F.hiddenprints
blood_DNA = blood_DNA | F.blood_DNA
fibers = fibers | F.fibers
scents = scents | F.scents
fingerprints = LAZY_LISTS_OR(fingerprints, F.fingerprints)
hiddenprints = LAZY_LISTS_OR(hiddenprints, F.hiddenprints)
blood_DNA = LAZY_LISTS_OR(blood_DNA, F.blood_DNA)
fibers = LAZY_LISTS_OR(fibers, F.fibers)
check_blood()
check_blood()
return ..()
@@ -48,10 +48,15 @@
/datum/component/forensics/proc/wipe_blood_DNA()
blood_DNA = null
if(isitem(parent))
qdel(parent.GetComponent(/datum/component/decal/blood))
return TRUE
/datum/component/forensics/proc/is_bloody(datum/source, clean_types)
if(!isitem(parent))
return FALSE
return length(blood_DNA) > 0
/datum/component/forensics/proc/wipe_fibers()
fibers = null
return TRUE
@@ -193,7 +198,7 @@
return
if(!length(blood_DNA))
return
parent.LoadComponent(/datum/component/decal/blood)
parent.AddElement(/datum/element/decal/blood, _color = get_blood_dna_color(blood_DNA))
//yog code for olfaction
/datum/component/forensics/proc/wipe_scents()

View File

@@ -344,7 +344,7 @@
var/datum/data/record/M = new()
M.fields["id"] = id
M.fields["name"] = record_name
M.fields["blood_type"] = H.dna.blood_type
M.fields["blood_type"] = H.dna.blood_type.name
M.fields["b_dna"] = H.dna.unique_enzymes
M.fields["mi_dis"] = "None"
M.fields["mi_dis_d"] = "No minor disabilities have been declared."

View File

@@ -52,7 +52,7 @@ 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
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
///first value is mutant color

View File

@@ -26,7 +26,15 @@
return ELEMENT_INCOMPATIBLE
SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
if(element_flags & ELEMENT_DETACH_ON_HOST_DESTROY)
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(OnTargetDelete), override = TRUE)
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(Detach), override = TRUE)
/*
The override = TRUE here is to suppress runtimes happening because of the blood decal element
being applied multiple times to a same thing every time there is some bloody attacks,
which happens due to ludicrous use of check_blood() in forensics.dm,
and how elements system is design and coded; there isn't exactly a not-hacky
way to determine whether a datum has this particular element before adding it...
*/
/datum/element/proc/OnTargetDelete(datum/source, force)
SIGNAL_HANDLER

View File

@@ -0,0 +1,106 @@
/datum/element/decal
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
argument_hash_start_idx = 2
/// Whether this decal can be cleaned.
var/cleanable
/// A description this decal appends to the target's examine message.
var/description
/// The overlay applied by this decal to the target.
var/mutable_appearance/pic
/*
A short lecture on decal element collision on rotation
If a given decal's rotated version is identical to one of existing (at a same target), pre-rotation decals,
then the rotated decal won't stay after when the colliding pre-rotation decal gets rotated,
resulting in some decal elements colliding into nonexistence. This internal tick-tock prevents
such collision by forcing a non-collision.
*/
var/rotated
/datum/element/decal/Attach(atom/target, _icon, _icon_state, _dir, _cleanable=FALSE, _color, _layer=TURF_LAYER, _description, _alpha=255, _rotated=FALSE)
. = ..()
if(!isatom(target) || (pic ? FALSE : !generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha, target)))
return ELEMENT_INCOMPATIBLE
description = _description
cleanable = _cleanable
rotated = _rotated
RegisterSignal(target,COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(apply_overlay), TRUE)
if(isturf(target))
RegisterSignal(target,COMSIG_TURF_AFTER_SHUTTLE_MOVE, PROC_REF(shuttlemove_react), TRUE)
if(target.flags_1 & INITIALIZED_1)
target.update_icon() //could use some queuing here now maybe.
else
RegisterSignal(target,COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, PROC_REF(late_update_icon), TRUE)
if(isitem(target))
INVOKE_ASYNC(target, /obj/item/proc/update_slot_icon, TRUE)
if(_dir)
RegisterSignal(target, COMSIG_ATOM_DIR_CHANGE, PROC_REF(rotate_react),TRUE)
if(_cleanable)
RegisterSignal(target, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_react),TRUE)
if(_description)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(examine),TRUE)
/**
* ## generate_appearance
*
* If the decal was not given an appearance, it will generate one based on the other given arguments.
* element won't be compatible if it cannot do either
* all args are fed into creating an image, they are byond vars for images you'll recognize in the byond docs
* (except source, source is the object whose appearance we're copying.)
*/
/datum/element/decal/proc/generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha, source)
if(!_icon || !_icon_state)
return FALSE
var/temp_image = image(_icon, null, _icon_state, _layer, _dir)
pic = new(temp_image)
pic.color = _color
pic.alpha = _alpha
pic.appearance_flags |= RESET_COLOR
return TRUE
/datum/element/decal/Detach(atom/source, force)
UnregisterSignal(source, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_TURF_AFTER_SHUTTLE_MOVE))
source.update_icon()
if(isitem(source))
INVOKE_ASYNC(source, /obj/item/proc/update_slot_icon)
return ..()
/datum/element/decal/proc/late_update_icon(atom/source)
SIGNAL_HANDLER
if(source && istype(source))
source.update_icon()
UnregisterSignal(source,COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE)
/datum/element/decal/proc/apply_overlay(atom/source, list/overlay_list)
SIGNAL_HANDLER
overlay_list += pic
/datum/element/decal/proc/shuttlemove_react(datum/source, turf/newT)
SIGNAL_HANDLER
Detach(source)
newT.AddElement(/datum/element/decal, pic.icon, pic.icon_state, pic.dir, cleanable, pic.color, pic.layer, description, pic.alpha, rotated)
/datum/element/decal/proc/rotate_react(datum/source, old_dir, new_dir)
SIGNAL_HANDLER
if(old_dir == new_dir)
return
Detach(source)
source.AddElement(/datum/element/decal, pic.icon, pic.icon_state, angle2dir(dir2angle(pic.dir)+dir2angle(new_dir)-dir2angle(old_dir)), cleanable, pic.color, pic.layer, description, pic.alpha, !rotated)
/datum/element/decal/proc/clean_react(datum/source, clean_types)
SIGNAL_HANDLER
if(clean_types & cleanable)
Detach(source)
return TRUE
return NONE
/datum/element/decal/proc/examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
examine_list += description

View File

@@ -0,0 +1,44 @@
/datum/element/decal/blood
/datum/element/decal/blood/Attach(datum/target, _icon, _icon_state, _dir, _cleanable=CLEAN_TYPE_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
if(!isitem(target))
return ELEMENT_INCOMPATIBLE
. = ..()
RegisterSignal(target, COMSIG_ATOM_GET_EXAMINE_NAME, PROC_REF(get_examine_name), TRUE)
/datum/element/decal/blood/Detach(atom/source, force)
UnregisterSignal(source, COMSIG_ATOM_GET_EXAMINE_NAME)
return ..()
/datum/element/decal/blood/generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha, source)
var/obj/item/I = source
if(!_icon)
_icon = 'icons/effects/blood.dmi'
if(!_icon_state)
_icon_state = "itemblood"
if(!_color)
_color = COLOR_BLOOD
var/item_icon = I.icon
var/item_icon_state = I.icon_state
var/static/list/blood_splatter_appearances = list()
//try to find a pre-processed blood-splatter. otherwise, make a new one
var/index = "[REF(item_icon)]-[item_icon_state]"
pic = blood_splatter_appearances[index]
if(!pic)
var/icon/blood_splatter_icon = icon(I.icon, I.icon_state, null, 1) //we only want to apply blood-splatters to the initial icon_state for each object
blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent)
blood_splatter_icon.Blend(icon(_icon, _icon_state), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant
pic = mutable_appearance(blood_splatter_icon, initial(I.icon_state))
pic.color = _color
blood_splatter_appearances[index] = pic
return TRUE
/datum/element/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override)
SIGNAL_HANDLER
var/atom/A = source
override[EXAMINE_POSITION_ARTICLE] = A.gender == PLURAL? "some" : "a"
override[EXAMINE_POSITION_BEFORE] = " blood-stained "
return COMPONENT_EXNAME_CHANGED

View File

@@ -732,11 +732,11 @@
/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("Y-"))
///Get the mobs dna list
/mob/living/carbon/get_blood_dna_list()
if(!(get_blood_id() in list(/datum/reagent/blood, /datum/reagent/toxin/acid))) //polysmorphs have DNA located in literal acid, don't ask me why
if(!(get_blood_id() in list(/datum/reagent/blood, /datum/reagent/toxin/acid, /datum/reagent/consumable/liquidelectricity))) //polysmorphs have DNA located in literal acid, don't ask me why
return
var/list/blood_dna = list()
if(dna)
@@ -746,10 +746,10 @@
return blood_dna
/mob/living/carbon/alien/get_blood_dna_list()
return list("UNKNOWN DNA" = "X*")
return list("UNKNOWN DNA" = get_blood_type("X"))
/mob/living/silicon/get_blood_dna_list()
return list("MOTOR OIL" = "SAE 5W-30") //just a little flavor text.
return list("SYNTHETIC COOLANT" = get_blood_type("Coolant"))
///to add a mob's dna info into an object's blood_dna list.
/atom/proc/transfer_mob_blood_dna(mob/living/L)

View File

@@ -102,7 +102,7 @@
data["patient"]["stat"] = "Dead"
data["patient"]["statstate"] = "bad"
data["patient"]["health"] = patient.health
data["patient"]["blood_type"] = patient.dna.blood_type
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()

View File

@@ -1026,7 +1026,7 @@
"UE"=scanner_occupant.dna.unique_enzymes,
"UF"=scanner_occupant.dna.unique_features,
"name"=scanner_occupant.real_name,
"blood_type"=scanner_occupant.dna.blood_type)
"blood_type"=scanner_occupant.dna.blood_type.name)
return

View File

@@ -546,7 +546,8 @@
if(3)
R.fields["age"] = rand(AGE_MIN, AGE_MAX)
if(4)
R.fields["blood_type"] = random_blood_type()
var/datum/blood_type/blood = random_blood_type()
R.fields["blood_type"] = blood.name
if(5)
R.fields["p_stat"] = pick("*Unconscious*", "Active", "Physically Unfit")
if(6)

View File

@@ -197,7 +197,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((altPatient.blood_volume / BLOOD_VOLUME_NORMAL(altPatient))*100)
var/blood_type = altPatient.dna.blood_type
var/blood_type = altPatient.dna.blood_type.name
var/blood_warning = " "
for(var/thing in altPatient.diseases) //Disease Information

View File

@@ -129,7 +129,11 @@
if(isalien(target))
new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target.drop_location(), splatter_dir)
else
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir)
var/splatter_color = null
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
splatter_color = carbon_target.dna.blood_type.color
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir, splatter_color)
//organs go everywhere
if(target_part && prob(10 * drill_level))

View File

@@ -83,25 +83,11 @@
//Add "bloodiness" of this blood's type, to the human's shoes
//This is on /cleanable because fuck this ancient mess
/obj/effect/decal/cleanable/Crossed(atom/movable/O)
..()
if(ishuman(O))
var/mob/living/carbon/human/H = O
if(H.shoes && blood_state && bloodiness && !HAS_TRAIT(H, TRAIT_LIGHT_STEP))
var/obj/item/clothing/shoes/S = H.shoes
if(!istype(S) || !S.can_be_bloody)
return
var/add_blood = 0
if(bloodiness >= BLOOD_GAIN_PER_STEP)
add_blood = BLOOD_GAIN_PER_STEP
else
add_blood = bloodiness
bloodiness -= add_blood
S.bloody_shoes[blood_state] = min(MAX_SHOE_BLOODINESS,S.bloody_shoes[blood_state]+add_blood)
S.add_blood_DNA(return_blood_DNA())
S.blood_state = blood_state
update_appearance(UPDATE_ICON)
H.update_inv_shoes()
/obj/effect/decal/cleanable/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
if(iscarbon(AM) && blood_state && bloodiness > 40)
SEND_SIGNAL(AM, COMSIG_STEP_ON_BLOOD, src)
update_icon()
/**

View File

@@ -10,8 +10,8 @@
blood_state = BLOOD_STATE_XENO
/obj/effect/decal/cleanable/xenoblood/Initialize(mapload)
add_blood_DNA(list("UNKNOWN DNA" = get_blood_type("X"))) // Needs to happen before ..()
. = ..()
add_blood_DNA(list("UNKNOWN DNA" = "X*"))
/obj/effect/decal/cleanable/xenoblood/xsplatter
random_icon_states = list("xgibbl1", "xgibbl2", "xgibbl3", "xgibbl4", "xgibbl5")
@@ -71,10 +71,6 @@
icon_state = "xgiblarvatorso"
random_icon_states = list("xgiblarvahead", "xgiblarvatorso")
/obj/effect/decal/cleanable/blood/xtracks
/obj/effect/decal/cleanable/xenoblood/xtracks
icon_state = "xtracks"
random_icon_states = null
/obj/effect/decal/cleanable/blood/xtracks/Initialize(mapload)
. = ..()
add_blood_DNA(list("Unknown DNA" = "X*"))

View File

@@ -1,44 +1,57 @@
/obj/effect/decal/cleanable/blood
name = "blood"
desc = "It's red and gooey. Perhaps it's the chef's cooking?"
desc = "It's weird and gooey. Perhaps it's the chef's cooking?"
icon = 'icons/effects/blood.dmi'
icon_state = "floor1"
color = COLOR_BLOOD
random_icon_states = list("floor1", "floor2", "floor3", "floor4", "floor5", "floor6", "floor7")
blood_state = BLOOD_STATE_HUMAN
bloodiness = BLOOD_AMOUNT_PER_DECAL
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
/obj/effect/decal/cleanable/blood/Initialize(mapload)
. = ..()
if(bloodiness)
start_drying()
else
dry()
/obj/effect/decal/cleanable/blood/process()
if(world.time > drytime)
dry()
/obj/effect/decal/cleanable/blood/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/decal/cleanable/blood/proc/get_timer()
drytime = world.time + 3 MINUTES
/obj/effect/decal/cleanable/blood/proc/start_drying()
get_timer()
START_PROCESSING(SSobj, src)
/obj/effect/decal/cleanable/blood/proc/dry()
if(bloodiness > 20)
bloodiness -= BLOOD_AMOUNT_PER_DECAL
get_timer()
else
name = dryname
desc = drydesc
bloodiness = 0
var/temp_color = ReadHSV(RGBtoHSV(color || COLOR_WHITE))
color = HSVtoRGB(hsv(temp_color[1], temp_color[2], max(temp_color[3] - 100,min(temp_color[3],10))))
STOP_PROCESSING(SSobj, src)
/obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C)
C.add_blood_DNA(return_blood_DNA())
if (bloodiness)
if (C.bloodiness < MAX_SHOE_BLOODINESS)
C.bloodiness += bloodiness
C.bloodiness = min((C.bloodiness + bloodiness), BLOOD_AMOUNT_PER_DECAL)
return ..()
/obj/effect/decal/cleanable/whiteblood
name = "\"blood\""
desc = "It's an unsettling colour. Maybe it's the chef's cooking?"
icon = 'icons/effects/blood.dmi'
icon_state = "genericsplatter1"
random_icon_states = list("genericsplatter1", "genericsplatter2", "genericsplatter3", "genericsplatter4", "genericsplatter5", "genericsplatter6")
/obj/effect/decal/cleanable/whiteblood/ethereal
name = "glowing \"blood\""
desc = "It has a fading glow. Surely it's just the chef's cooking?"
light_power = 1
light_range = 2
light_color = "#eef442"
/obj/effect/decal/cleanable/whiteblood/ethereal/Initialize(mapload, list/datum/disease/diseases)
. = ..()
add_atom_colour(light_color, FIXED_COLOUR_PRIORITY)
addtimer(CALLBACK(src, PROC_REF(Fade)), 1 MINUTES)
/obj/effect/decal/cleanable/whiteblood/ethereal/proc/Fade()
name = "faded \"blood\""
light_power = 0
light_range = 0
update_light()
/obj/effect/decal/cleanable/blood/old
name = "dried blood"
desc = "Looks like it's been here a while. Eew."
@@ -56,6 +69,8 @@
/obj/effect/decal/cleanable/blood/splatter
icon_state = "gibbl1"
random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5")
dryname = "dried tracks"
drydesc = "Some old bloody tracks left by wheels. Machines are evil, perhaps."
/obj/effect/decal/cleanable/blood/splatter/over_window // special layer/plane set to appear on windows
layer = ABOVE_WINDOW_LAYER
@@ -69,25 +84,23 @@
icon_state = "tracks"
random_icon_states = null
/obj/effect/decal/cleanable/trail_holder //not a child of blood on purpose
/obj/effect/decal/cleanable/blood/trail_holder //not a child of blood on purpose //nice fucking descriptive comment jackass, fuck you //hello fikou //this is cowbot
name = "blood"
icon = 'icons/effects/blood.dmi'
desc = "Your instincts say you shouldn't be following these."
icon_state = null
random_icon_states = null
var/list/existing_dirs = list()
/obj/effect/decal/cleanable/trail_holder/can_bloodcrawl_in()
return TRUE
/obj/effect/decal/cleanable/trail_holder/proc/Etherealify()
/obj/effect/decal/cleanable/blood/proc/Etherealify()
name = "glowing \"blood\""
light_power = 1
light_range = 2
light_range = 1
light_color = "#eef442"
update_light()
add_atom_colour(light_color, FIXED_COLOUR_PRIORITY)
addtimer(CALLBACK(src, PROC_REF(Fade)), 1 MINUTES)
addtimer(CALLBACK(src, PROC_REF(Fade)), 3 MINUTES)
/obj/effect/decal/cleanable/trail_holder/proc/Fade()
/obj/effect/decal/cleanable/blood/proc/Fade()
name = "faded \"blood\""
light_power = 0
light_range = 0
@@ -102,21 +115,17 @@
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6")
mergeable_decal = FALSE
var/already_rotting = FALSE
dryname = "rotting gibs"
drydesc = "They look bloody and gruesome while some terrible smell fills the air."
/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases)
. = ..()
reagents.add_reagent(/datum/reagent/liquidgibs, 5)
if(already_rotting)
start_rotting(rename=FALSE)
else
addtimer(CALLBACK(src, PROC_REF(start_rotting)), 2 MINUTES)
var/mutable_appearance/gib_overlay = mutable_appearance(icon, "[icon_state]-overlay", appearance_flags = RESET_COLOR)
add_overlay(gib_overlay)
/obj/effect/decal/cleanable/blood/gibs/proc/start_rotting(rename=TRUE)
if(rename)
name = "rotting [initial(name)]"
desc += " They smell terrible."
AddComponent(/datum/component/rot/gibs)
/obj/effect/decal/cleanable/blood/gibs/dry()
. = ..()
/obj/effect/decal/cleanable/blood/gibs/ex_act(severity, target)
return
@@ -131,11 +140,13 @@
var/list/diseases = list()
SEND_SIGNAL(src, COMSIG_GIBS_STREAK, directions, diseases)
var/direction = pick(directions)
for(var/i in 0 to pick(0, 200; 1, 150; 2, 50))
for(var/i in 0 to pick(0, 1, 2))
if (!mapload)
sleep(0.2 SECONDS)
if(i > 0)
new /obj/effect/decal/cleanable/blood/splatter(loc, diseases)
var/obj/effect/decal/cleanable/blood/splatter/splat = new /obj/effect/decal/cleanable/blood/splatter(loc, diseases)
if(!QDELETED(splat) && HAS_BLOOD_DNA(src))
splat.add_blood_DNA(src.return_blood_DNA())
if(!step_to(src, get_step(src, direction), 0))
break
@@ -166,13 +177,14 @@
/obj/effect/decal/cleanable/blood/gibs/old
name = "old rotting gibs"
desc = "Space Jesus, why didn't anyone clean this up? They smell terrible."
icon_state = "gib1-old"
bloodiness = 0
already_rotting = TRUE
dryname = "old rotting gibs"
drydesc = "Space Jesus, why didn't anyone clean this up? They smell terrible."
/obj/effect/decal/cleanable/blood/gibs/old/Initialize(mapload, list/datum/disease/diseases)
. = ..()
setDir(pick(1,2,4,8))
icon_state += "-old"
add_blood_DNA(list("Non-human DNA" = random_blood_type()))
/obj/effect/decal/cleanable/blood/drip
@@ -182,6 +194,8 @@
random_icon_states = list("drip1","drip2","drip3","drip4","drip5")
bloodiness = 0
var/drips = 1
dryname = "drips of blood"
drydesc = "It's red."
/obj/effect/decal/cleanable/blood/drip/can_bloodcrawl_in()
return TRUE
@@ -198,32 +212,13 @@
blood_state = BLOOD_STATE_HUMAN //the icon state to load images from
var/entered_dirs = 0
var/exited_dirs = 0
/// List of shoe or other clothing that covers feet types that have made footprints here.
var/list/shoe_types = list()
/// List of species that have made footprints here.
var/list/species_types = list()
/obj/effect/decal/cleanable/blood/footprints/Crossed(atom/movable/O)
..()
if(ishuman(O))
var/mob/living/carbon/human/H = O
var/obj/item/clothing/shoes/S = H.shoes
if(S && S.bloody_shoes[blood_state])
S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0)
shoe_types |= S.type
if (!(entered_dirs & H.dir))
entered_dirs |= H.dir
update_appearance(UPDATE_ICON)
/obj/effect/decal/cleanable/blood/footprints/Uncrossed(atom/movable/O)
..()
if(ishuman(O))
var/mob/living/carbon/human/H = O
var/obj/item/clothing/shoes/S = H.shoes
if(S && istype(S) && S.bloody_shoes[blood_state])
S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0)
shoe_types |= S.type
if (!(exited_dirs & H.dir))
exited_dirs |= H.dir
update_appearance(UPDATE_ICON)
dryname = "dried footprints"
drydesc = "HMM... SOMEONE WAS HERE!"
/obj/effect/decal/cleanable/blood/footprints/update_overlays()
. = ..()
@@ -240,21 +235,32 @@
GLOB.bloody_footprints_cache["exited-[blood_state]-[Ddir]"] = bloodstep_overlay = image(icon, "[blood_state]2", dir = Ddir)
. += bloodstep_overlay
alpha = BLOODY_FOOTPRINT_BASE_ALPHA + bloodiness
alpha = min(BLOODY_FOOTPRINT_BASE_ALPHA + (255 - BLOODY_FOOTPRINT_BASE_ALPHA) * bloodiness / (BLOOD_ITEM_MAX / 2), 255)
/obj/effect/decal/cleanable/blood/footprints/examine(mob/user)
. = ..()
if(shoe_types.len)
. += "You recognise the footprints as belonging to:\n"
for(var/shoe in shoe_types)
var/obj/item/clothing/shoes/S = shoe
. += "[icon2html(initial(S.icon), user)] Some <B>[initial(S.name)]</B>.\n"
if((shoe_types.len + species_types.len) > 0)
. += "You recognise the footprints 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 == "monkey")
. += "[icon2html('icons/mob/monkey.dmi', user, "monkey1")] Some <B>monkey feet</B>."
else if(species == "human")
. += "[icon2html('icons/mob/human_parts.dmi', user, "default_human_l_leg")] Some <B>human feet</B>."
else
. += "[icon2html('icons/mob/human_parts.dmi', user, "[species]_l_leg")] Some <B>[species] feet</B>."
/obj/effect/decal/cleanable/blood/footprints/replace_decal(obj/effect/decal/cleanable/C)
if(blood_state != C.blood_state) //We only replace footprints of the same type as us
return
..()
return FALSE
return ..()
/obj/effect/decal/cleanable/blood/footprints/can_bloodcrawl_in()
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
@@ -277,9 +283,11 @@
/// Insurance so that we don't keep moving once we hit a stoppoint
var/hit_endpoint = FALSE
/obj/effect/decal/cleanable/blood/hitsplatter/Initialize(mapload, splatter_strength)
/obj/effect/decal/cleanable/blood/hitsplatter/Initialize(mapload, splatter_strength, set_color)
. = ..()
prev_loc = loc //Just so we are sure prev_loc exists
if(set_color)
color = set_color
if(splatter_strength)
src.splatter_strength = splatter_strength
@@ -343,6 +351,7 @@
land_on_window(bumped_atom)
else
var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new(prev_loc)
final_splatter.color = color
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.
@@ -354,6 +363,7 @@
if(!the_window.fulltile)
return
var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new
final_splatter.color = color
final_splatter.forceMove(the_window)
the_window.vis_contents += final_splatter
the_window.bloodied = TRUE

View File

@@ -34,12 +34,29 @@
/obj/effect/turf_decal
icon = 'icons/turf/decals.dmi'
icon_state = "warningline"
plane = FLOOR_PLANE
layer = TURF_DECAL_LAYER
var/detail_overlay
var/detail_color
// This is with the intent of optimizing mapload
// See spawners for more details since we use the same pattern
// Basically rather then creating and deleting ourselves, why not just do the bare minimum?
/obj/effect/turf_decal/Initialize(mapload)
. = ..()
SHOULD_CALL_PARENT(FALSE)
if(flags_1 & INITIALIZED_1)
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.AddComponent(/datum/component/decal, icon, icon_state, dir, FALSE, color, null, null, alpha)
T.AddElement(/datum/element/decal, icon, icon_state, dir, FALSE, color, null, null, alpha, FALSE)
if(detail_overlay)
T.AddElement(/datum/element/decal, icon, detail_overlay, dir, FALSE, detail_color, null, null, alpha, appearance_flags)
return INITIALIZE_HINT_QDEL
/obj/effect/turf_decal/Destroy(force)
SHOULD_CALL_PARENT(FALSE)
moveToNullspace()
return QDEL_HINT_QUEUE

View File

@@ -4,6 +4,7 @@
duration = 0.5 SECONDS
randomdir = FALSE
layer = BELOW_MOB_LAYER
color = COLOR_BLOOD
var/splatter_type = "splatter"
/obj/effect_temp_visual/dir_setting/tentacle
@@ -11,12 +12,14 @@
icon_state = "Goliath_tentacle_spawn"
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir)
/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir, set_color)
if(set_dir in GLOB.diagonals)
icon_state = "[splatter_type][pick(1, 2, 6)]"
else
icon_state = "[splatter_type][pick(3, 4, 5)]"
. = ..()
if(set_color)
color = set_color
var/target_pixel_x = 0
var/target_pixel_y = 0
switch(set_dir)
@@ -47,6 +50,7 @@
/obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter
splatter_type = "xsplatter"
color = null
/obj/effect/temp_visual/dir_setting/bloodsplatter/genericsplatter
splatter_type = "genericsplatter"

View File

@@ -427,7 +427,7 @@ GENE SCANNER
if(H.is_bleeding())
combined_msg += span_danger("Subject is losing blood at a rate of [H.get_total_bleed_rate() * H.physiology?.bleed_mod] cl per process!")
var/blood_percent = round((C.blood_volume / BLOOD_VOLUME_NORMAL(C))*100)
var/blood_type = C.dna.blood_type
var/blood_type = C.dna.blood_type.name
if(blood_id != /datum/reagent/blood)//special blood substance
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
if(R)

View File

@@ -662,3 +662,13 @@ GLOBAL_LIST_EMPTY(station_turfs)
/// Called when attempting to set fire to a turf
/turf/proc/IgniteTurf(power, fire_color="red")
return
/turf/proc/on_turf_saved()
// This is all we can do. I'm sorry mappers, but there's no way to get any more details.
var/first = TRUE
for(var/datum/element/decal/decal as anything in GetComponents(/datum/element/decal))
if(!first)
. += ",\n"
. += "[/obj/effect/turf_decal]{\n\ticon = '[decal.pic.icon]';\n\ticon_state = \"[decal.pic.icon_state]\";\n\tdir = [decal.pic.dir];\n\tcolor = \"[decal.pic.color]\"\n\t}"
first = FALSE
return

View File

@@ -237,7 +237,7 @@
dat += "<table cellspacing=5><tr><th>Name</th><th>DNA</th><th>Blood Type</th></tr>"
for(var/mob/living/carbon/human/H in GLOB.carbon_list)
if(H.ckey)
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.dna.blood_type]</td></tr>"
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.dna.blood_type.name]</td></tr>"
dat += "</table>"
dat += "</BODY></HTML>"
mob_user << browse(dat, "window=DNA;size=440x410")

View File

@@ -763,12 +763,8 @@
temp += max((B.bloodiness**2)/800,1)
new /obj/effect/temp_visual/cult/turf/floor(get_turf(B))
qdel(B)
for(var/obj/effect/decal/cleanable/trail_holder/TH in view(T, 2))
for(var/obj/effect/decal/cleanable/blood/trail_holder/TH in view(T, 2))
qdel(TH)
var/obj/item/clothing/shoes/shoecheck = user.shoes
if(shoecheck && istype(shoecheck) && shoecheck.bloody_shoes[/datum/reagent/blood])
temp += shoecheck.bloody_shoes[/datum/reagent/blood]/20
shoecheck.bloody_shoes[/datum/reagent/blood] = 0
if(temp)
user.Beam(T,icon_state="drainbeam",time=15)
new /obj/effect/temp_visual/cult/sparks(get_turf(user))

View File

@@ -26,6 +26,8 @@
var/clothing_flags = NONE
var/can_be_bloody = TRUE
/// What items can be consumed to repair this clothing (must by an /obj/item/stack)
var/repairable_by = /obj/item/stack/sheet/cloth
@@ -61,6 +63,8 @@
. = ..()
if(ispath(pocket_storage_component_path))
LoadComponent(pocket_storage_component_path)
if(can_be_bloody && ((body_parts_covered & FEET) || (flags_inv & HIDESHOES)))
LoadComponent(/datum/component/bloodysoles)
/obj/item/clothing/MouseDrop(atom/over_object)
. = ..()

View File

@@ -32,7 +32,9 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves")
if(HAS_BLOOD_DNA(src))
. += mutable_appearance('icons/effects/blood.dmi', "bloodyhands")
var/mutable_appearance/bloody_hands = mutable_appearance('icons/effects/blood.dmi', "bloodyhands")
bloody_hands.color = get_blood_dna_color(return_blood_DNA())
. += bloody_hands
/obj/item/clothing/gloves/update_clothes_damaged_state()
..()

View File

@@ -23,8 +23,10 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet")
if(HAS_BLOOD_DNA(src))
. += mutable_appearance('icons/effects/blood.dmi', "helmetblood")
var/mutable_appearance/bloody_helmet = mutable_appearance('icons/effects/blood.dmi', "helmetblood")
bloody_helmet.color = get_blood_dna_color(return_blood_DNA())
. += bloody_helmet
/obj/item/clothing/head/update_clothes_damaged_state()
..()
if(ismob(loc))

View File

@@ -37,7 +37,9 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask")
if(HAS_BLOOD_DNA(src))
. += mutable_appearance('icons/effects/blood.dmi', "maskblood")
var/mutable_appearance/bloody_mask = mutable_appearance('icons/effects/blood.dmi', "maskblood")
bloody_mask.color = get_blood_dna_color(return_blood_DNA())
. += bloody_mask
/obj/item/clothing/mask/update_clothes_damaged_state()
..()

View File

@@ -13,7 +13,9 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask")
if(HAS_BLOOD_DNA(src))
. += mutable_appearance('icons/effects/blood.dmi', "maskblood")
var/mutable_appearance/bloody_mask = mutable_appearance('icons/effects/blood.dmi', "maskblood")
bloody_mask.color = get_blood_dna_color(return_blood_DNA())
. += bloody_mask
/obj/item/clothing/neck/tie
name = "tie"

View File

@@ -9,11 +9,9 @@
slot_flags = ITEM_SLOT_FEET
slowdown = SHOES_SLOWDOWN
var/blood_state = BLOOD_STATE_NOT_BLOODY
var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
var/offset = 0
var/equipped_before_drop = FALSE
var/can_be_bloody = TRUE
var/xenoshoe = NO_DIGIT // Check for if shoes can be worn by straight legs (NO_DIGIT) which is default, both / hybrid (EITHER_STYLE), or digitigrade only (YES_DIGIT)
var/mutantrace_variation = NO_MUTANTRACE_VARIATION // Assigns shoes to have variations for if worn clothing doesn't enforce straight legs (such as cursed jumpskirts)
var/adjusted = NORMAL_STYLE // Default needed to make the above work
@@ -41,16 +39,13 @@
/obj/item/clothing/shoes/worn_overlays(isinhands = FALSE)
. = list()
if(!isinhands)
var/bloody = FALSE
if(HAS_BLOOD_DNA(src))
bloody = TRUE
else
bloody = bloody_shoes[BLOOD_STATE_HUMAN]
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe")
if(bloody)
. += mutable_appearance('icons/effects/blood.dmi', "shoeblood")
if(HAS_BLOOD_DNA(src))
var/mutable_appearance/bloody_shoes
bloody_shoes = mutable_appearance('icons/effects/blood.dmi', "shoeblood")
bloody_shoes.color = get_blood_dna_color(return_blood_DNA())
. += bloody_shoes
/obj/item/clothing/shoes/equipped(mob/user, slot)
if(adjusted)
@@ -89,16 +84,5 @@
var/mob/M = loc
M.update_inv_shoes()
/obj/item/clothing/shoes/wash(clean_types)
. = ..()
if(!(clean_types & CLEAN_TYPE_BLOOD) || blood_state == BLOOD_STATE_NOT_BLOODY)
return
bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
blood_state = BLOOD_STATE_NOT_BLOODY
if(ismob(loc))
var/mob/M = loc
M.update_inv_shoes()
return TRUE
/obj/item/proc/negates_gravity()
return FALSE

View File

@@ -25,9 +25,11 @@
. = list()
if(!isinhands)
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damaged[blood_overlay_type]")
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
if(HAS_BLOOD_DNA(src))
. += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
var/mutable_appearance/bloody_armor = mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
bloody_armor.color = get_blood_dna_color(return_blood_DNA())
. += bloody_armor
var/mob/living/carbon/human/M = loc
if(ishuman(M) && M.w_uniform)
var/obj/item/clothing/under/U = M.w_uniform

View File

@@ -27,7 +27,9 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
if(HAS_BLOOD_DNA(src))
. += mutable_appearance('icons/effects/blood.dmi', "uniformblood")
var/mutable_appearance/bloody_uniform = mutable_appearance('icons/effects/blood.dmi', "uniformblood")
bloody_uniform.color = get_blood_dna_color(return_blood_DNA())
. += bloody_uniform
if(accessory_overlay)
. += accessory_overlay

View File

@@ -77,8 +77,9 @@
var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src
if(!B)
B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases)
B.add_blood_DNA(blood_dna) //give blood info to the blood decal.
return TRUE //we bloodied the floor
if(!QDELETED(B))
B.add_blood_DNA(blood_dna) //give blood info to the blood decal.
return TRUE //we bloodied the floor
/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases)
if(wear_suit)
@@ -96,6 +97,11 @@
update_inv_gloves() //handles bloody hands overlays and updating
return TRUE
/obj/effect/decal/cleanable/blood/add_blood_DNA(list/blood_dna, list/datum/disease/diseases)
. = ..()
if(blood_dna)
color = get_blood_dna_color(blood_dna)
/atom/proc/transfer_fingerprints_to(atom/A)
A.add_fingerprint_list(return_fingerprints())
A.add_hiddenprint_list(return_hiddenprints())

View File

@@ -178,7 +178,7 @@
var/list/data = null
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("O-"))
if(rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin)
// apple tastes of apple.
if(istype(T, /obj/item/reagent_containers/food/snacks/grown))

View File

@@ -1376,7 +1376,7 @@ GLOBAL_LIST_EMPTY(aide_list)
if(target == user)
continue
for(var/obj/effect/decal/cleanable/decal in range(0, target))
if(istype(decal, /obj/effect/decal/cleanable/blood )|| istype(decal, /obj/effect/decal/cleanable/trail_holder))
if(istype(decal, /obj/effect/decal/cleanable/blood )|| istype(decal, /obj/effect/decal/cleanable/blood/trail_holder))
valid_reaching = TRUE
target.apply_status_effect(STATUS_EFFECT_KNUCKLED)
if(!valid_reaching)

View File

@@ -238,7 +238,8 @@
if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS))
continue
C.ForceContractDisease(D)
if(!(blood_data["blood_type"] in get_safe_blood(C.dna.blood_type)))
var/datum/blood_type/blood_type = blood_data["blood_type"]
if(!(blood_type.type in C.dna.blood_type.compatible_types))
C.reagents.add_reagent(/datum/reagent/toxin, amount * 0.5)
return TRUE
@@ -317,47 +318,25 @@
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
/proc/random_blood_type()
return get_blood_type(pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+"))
var/static/list/bloodtypes_safe = list(
"A-" = list("A-", "O-", "U"),
"A+" = list("A-", "A+", "O-", "O+", "U"),
"B-" = list("B-", "O-", "U"),
"B+" = list("B-", "B+", "O-", "O+", "U"),
"AB-" = list("A-", "B-", "O-", "AB-", "U"),
"AB+" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+", "U"),
"O-" = list("O-", "U"),
"O+" = list("O-", "O+", "U"),
"L" = list("L", "U"),
"U" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+", "L", "U") //literally universal
)
/proc/get_blood_type(type)
return GLOB.blood_types[type]
var/safe = bloodtypes_safe[bloodtype]
if(safe)
. = safe
/proc/get_blood_dna_color(list/blood_dna)
if(!blood_dna)
return null
var/blood_print = blood_dna[length(blood_dna)]
var/datum/blood_type/blood_type = blood_dna[blood_print]
if(!blood_type)
return null
return blood_type.color
//to add a splatter of blood or other mob liquid.
/mob/living/proc/add_splatter_floor(turf/T, small_drip)
if(!T)
T = get_turf(src)
if(ispolysmorph(src)) //polysmorphs bleed green blood
var/obj/effect/decal/cleanable/xenoblood/B = locate() in T.contents
if(!B)
B = new(T)
B.transfer_mob_blood_dna(src)
return
if(isethereal(src))
var/obj/effect/decal/cleanable/whiteblood/ethereal/B = locate() in T.contents
if(!B)
B = new(T)
B.transfer_mob_blood_dna(src)
return
if(get_blood_id() != /datum/reagent/blood)
return
var/list/temp_blood_DNA
if(small_drip)
// Only a certain number of drips (or one large splatter) can be on a given turf.
@@ -367,6 +346,8 @@
drop.drips++
drop.add_overlay(pick(drop.random_icon_states))
drop.transfer_mob_blood_dna(src)
if(isethereal(src))
drop.Etherealify()
return
else
temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter
@@ -374,17 +355,20 @@
else
drop = new(T, get_static_viruses())
drop.transfer_mob_blood_dna(src)
if(isethereal(src))
drop.Etherealify()
return
// Find a blood decal or create a new one.
var/obj/effect/decal/cleanable/blood/B = locate() in T
if(!B)
B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses())
if (B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit
B.bloodiness += BLOOD_AMOUNT_PER_DECAL
B.bloodiness = min((B.bloodiness + BLOOD_AMOUNT_PER_DECAL), BLOOD_POOL_MAX)
B.transfer_mob_blood_dna(src) //give blood info to the blood decal.
if(temp_blood_DNA)
B.add_blood_DNA(temp_blood_DNA)
if(isethereal(src))
B.Etherealify()
/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip)
if(!(NOBLOOD in dna.species.species_traits))

View File

@@ -1272,7 +1272,15 @@
/mob/living/carbon/proc/spray_blood(splatter_direction, splatter_strength = 3)
if(!isturf(loc))
return
var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc)
var/splatter_color = null
if(dna?.blood_type)
splatter_color = dna.blood_type.color
else
var/blood_id = get_blood_id()
if(blood_id != /datum/reagent/blood)//special blood substance
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
splatter_color = R.color
var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc, splatter_strength, splatter_color)
our_splatter.add_blood_DNA(return_blood_DNA())
our_splatter.blood_dna_info = get_blood_dna_list()
var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)

View File

@@ -29,13 +29,13 @@
if(with_bodyparts)
switch(dna.species.species_gibs)
if("human")
new /obj/effect/gibspawner/human(get_turf(src), dna, get_static_viruses())
new /obj/effect/gibspawner/human(get_turf(src), src, get_static_viruses())
if("robotic")
new /obj/effect/gibspawner/robot(get_turf(src))
else
switch(dna.species.species_gibs)
if("human")
new /obj/effect/gibspawner/human(get_turf(src), dna, get_static_viruses())
new /obj/effect/gibspawner/human(get_turf(src), src, get_static_viruses())
if("robotic")
new /obj/effect/gibspawner/robot(get_turf(src))

View File

@@ -33,6 +33,7 @@
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(clean_face))
AddComponent(/datum/component/personal_crafting)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6)
AddComponent(/datum/component/bloodysoles/feet)
/mob/living/carbon/human/proc/setup_human_dna()
//initialize dna. for spawned humans; overwritten by other code

View File

@@ -198,8 +198,6 @@
if(I.force && I.damtype != STAMINA && affecting.status == BODYPART_ROBOTIC) // Bodpart_robotic sparks when hit, but only when it does real damage
if(I.force >= 5)
do_sparks(1, FALSE, loc)
if(prob(25))
new /obj/effect/decal/cleanable/oil(loc)
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)

View File

@@ -59,32 +59,10 @@
/mob/living/carbon/human/Move(NewLoc, direct)
. = ..()
if(shoes)
if(mobility_flags & MOBILITY_STAND)
if(loc == NewLoc)
if(!has_gravity(loc))
return
var/obj/item/clothing/shoes/S = shoes
//Bloody footprints
var/turf/T = get_turf(src)
if(istype(S) && S.bloody_shoes && S.bloody_shoes[S.blood_state])
for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T)
if (oldFP.blood_state == S.blood_state)
return
//No oldFP or they're all a different kind of blood
S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP)
if (S.bloody_shoes[S.blood_state] > BLOOD_LOSS_IN_SPREAD)
var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T)
FP.blood_state = S.blood_state
FP.entered_dirs |= dir
FP.bloodiness = S.bloody_shoes[S.blood_state] - BLOOD_LOSS_IN_SPREAD
FP.add_blood_DNA(S.return_blood_DNA())
FP.update_appearance(UPDATE_ICON)
update_inv_shoes()
//End bloody footprints
if(istype(S))
S.step_action()
if(shoes && (mobility_flags & MOBILITY_STAND) && loc == NewLoc && has_gravity(loc))
var/obj/item/clothing/shoes/S = shoes
S.step_action()
if(wear_neck)
if(mobility_flags & MOBILITY_STAND)
if(loc == NewLoc)

View File

@@ -169,6 +169,10 @@
if(!not_handled)
I.equipped(src, slot, initial)
// Send a signal for when we equip an item that used to cover our feet/shoes. Used for bloody feet
if((I.body_parts_covered & FEET) || (I.flags_inv | I.transparent_protection) & HIDESHOES)
SEND_SIGNAL(src, COMSIG_CARBON_EQUIP_SHOECOVER, I, slot, initial)
return not_handled //For future deeper overrides
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
@@ -251,6 +255,10 @@
if(!QDELETED(src))
update_inv_s_store()
// Send a signal for when we unequip an item that used to cover our feet/shoes. Used for bloody feet
if((I.body_parts_covered & FEET) || (I.flags_inv | I.transparent_protection) & HIDESHOES)
SEND_SIGNAL(src, COMSIG_CARBON_UNEQUIP_SHOECOVER, I, force, newloc, no_move, invdrop, silent)
/mob/living/carbon/human/toggle_internals(obj/item/tank, is_external = FALSE)
// Just close the tank if it's the one the mob already has open.
var/obj/item/existing_tank = is_external ? external : internal

View File

@@ -204,6 +204,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
//Should we preload this species's organs?
var/preload = TRUE
///Does our species have colors for its' damage overlays?
var/use_damage_color = TRUE
///////////
// PROCS //
///////////
@@ -435,7 +438,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
regenerate_organs(C,old_species)
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
C.dna.blood_type = exotic_bloodtype
C.dna.blood_type = get_blood_type(exotic_bloodtype)
if(old_species.mutanthands)
for(var/obj/item/I in C.held_items)

View File

@@ -10,6 +10,7 @@
mutantheart = /obj/item/organ/heart/ethereal
mutanteyes = /obj/item/organ/eyes/ethereal
exotic_blood = /datum/reagent/consumable/liquidelectricity //Liquid Electricity. fuck you think of something better gamer
exotic_bloodtype = "E" //this isn't used for anything other than bloodsplatter colours
siemens_coeff = 0.5 //They thrive on energy
brutemod = 1.25 //Don't rupture their membranes
burnmod = 0.8 //Bodies are resilient to heat and energy

View File

@@ -6,6 +6,7 @@
inherent_traits = list(TRAIT_ACIDBLOOD, TRAIT_SKINNY)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
exotic_blood = /datum/reagent/toxin/acid //Hell yeah sulphuric acid blood
exotic_bloodtype = "X" //this isn't used for anything other than bloodsplatter colours
meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno
liked_food = GROSS | MEAT | MICE
disliked_food = GRAIN | DAIRY | VEGETABLES | FRUIT

View File

@@ -182,6 +182,7 @@ There are several things that need to be remembered:
bloody_overlay.icon_state = "bloodyhands_left"
else if(has_right_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_right"
bloody_overlay.color = get_blood_dna_color(return_blood_DNA())
overlays_standing[GLOVES_LAYER] = bloody_overlay

View File

@@ -45,6 +45,7 @@
create_dna(src)
dna.initialize_dna(random_blood_type())
AddComponent(/datum/component/bloodysoles/feet)
/mob/living/carbon/monkey/Destroy()
SSmobs.cubemonkeys -= src

View File

@@ -114,7 +114,10 @@
var/obj/item/bodypart/BP = X
if(BP.dmg_overlay_type)
if(BP.brutestate)
damage_overlay.add_overlay("[BP.dmg_overlay_type]_[BP.body_zone]_[BP.brutestate]0") //we're adding icon_states of the base image as overlays
var/image/brute_overlay = image('icons/mob/dam_mob.dmi', "[BP.dmg_overlay_type]_[BP.body_zone]_[BP.brutestate]0")
if(BP.use_damage_color)
brute_overlay.color = BP.damage_color
damage_overlay.add_overlay(brute_overlay)
if(BP.burnstate)
damage_overlay.add_overlay("[BP.dmg_overlay_type]_[BP.body_zone]_0[BP.burnstate]")

View File

@@ -669,7 +669,7 @@
/mob/living/proc/makeTrail(turf/target_turf, turf/start, direction)
if(!has_gravity() || !isturf(start) || !blood_volume)
return
var/blood_exists = locate(/obj/effect/decal/cleanable/trail_holder) in start
var/blood_exists = locate(/obj/effect/decal/cleanable/blood/trail_holder) in start
var/trail_type = getTrail()
if(!trail_type)
@@ -691,15 +691,15 @@
if((newdir in GLOB.cardinals) && (prob(50)))
newdir = turn(get_dir(target_turf, start), 180)
if(!blood_exists)
var/obj/effect/decal/cleanable/trail_holder/TH = new(start, get_static_viruses())
if(isethereal(src))//ethereal blood glows
TH.Etherealify()
new /obj/effect/decal/cleanable/blood/trail_holder(start, get_static_viruses())
for(var/obj/effect/decal/cleanable/trail_holder/TH in start)
for(var/obj/effect/decal/cleanable/blood/trail_holder/TH in start)
if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled)
TH.existing_dirs += newdir
TH.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir))
TH.transfer_mob_blood_dna(src)
if(isethereal(src))//ethereal blood glows
TH.Etherealify()
/mob/living/carbon/human/makeTrail(turf/T)
if((NOBLOOD in dna.species.species_traits) || !is_bleeding() || bleedsuppress)
@@ -720,16 +720,8 @@
/mob/living/proc/getTrail()
if(getBruteLoss() < 300)
if(ispolysmorph(src))
return pick("xltrails_1", "xltrails_2")
if(isethereal(src))
return pick("wltrails_1", "wltrails_2")
return pick("ltrails_1", "ltrails_2")
else
if(ispolysmorph(src))
return pick("xttrails_1", "xttrails_2")
if(isethereal(src))
return pick("wttrails_1", "wttrails_2")
return pick("trails_1", "trails_2")
/mob/living/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0)

View File

@@ -192,7 +192,7 @@
if(blood)
target_types += /obj/effect/decal/cleanable/xenoblood
target_types += /obj/effect/decal/cleanable/blood
target_types += /obj/effect/decal/cleanable/trail_holder
target_types += /obj/effect/decal/cleanable/blood/trail_holder
if(pests)
target_types += /mob/living/simple_animal/cockroach

View File

@@ -50,6 +50,9 @@
Changing this around would probably require a good look-over the pre-existing code.
*/
///How many legs does this mob currently have. Should only be changed through set_num_legs()
var/num_legs = 2
/// The zone this mob is currently targeting
var/zone_selected = BODY_ZONE_CHEST

View File

@@ -242,8 +242,11 @@
var/obj/item/bodypart/B = L.get_bodypart(def_zone)
if(B?.status == BODYPART_ROBOTIC) // So if you hit a robotic, it sparks instead of bloodspatters
do_sparks(2, FALSE, target.loc)
if(prob(25))
new /obj/effect/decal/cleanable/oil(target_loca)
var/splatter_color = null
if(iscarbon(L))
var/mob/living/carbon/carbon_target = L
splatter_color = carbon_target.dna.blood_type.color
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, splatter_color)
if(prob(33))
L.add_splatter_floor(target_loca)
else if(impact_effect_type && !hitscan)

View File

@@ -761,9 +761,10 @@
if(reac_volume < 3)
return
var/obj/effect/decal/cleanable/whiteblood/ethereal/B = locate() in T //find some blood here
var/obj/effect/decal/cleanable/blood/B = locate() in T
if(!B)
B = new(T)
B = new /obj/effect/decal/cleanable/blood/splatter(T)
B.Etherealify()
/datum/reagent/consumable/liquidelectricity/on_mob_life(mob/living/carbon/M)
if(HAS_TRAIT(M, TRAIT_POWERHUNGRY))

View File

@@ -1,7 +1,7 @@
/datum/reagent/blood
data = list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null,"quirks"=null)
name = "Blood"
color = "#C80000" // rgb: 200, 0, 0
color = COLOR_BLOOD
metabolization_rate = 5 //fast rate so it disappears fast.
taste_description = "iron"
taste_mult = 1.3
@@ -30,17 +30,22 @@
L.ForceContractDisease(D)
if(iscarbon(L))
var/mob/living/carbon/C = L
if(C.get_blood_id() == /datum/reagent/blood && ((methods & INJECT) || ((methods & INGEST) && C.dna && C.dna.species && (DRINKSBLOOD in C.dna.species.species_traits))))
if(!data || !(data["blood_type"] in get_safe_blood(C.dna.blood_type)) && !IS_BLOODSUCKER(C))
C.reagents.add_reagent(/datum/reagent/toxin, reac_volume * 0.5)
else
C.blood_volume = min(C.blood_volume + round(reac_volume, 0.1), BLOOD_VOLUME_MAXIMUM(C))
var/mob/living/carbon/exposed_carbon = L
if(exposed_carbon.get_blood_id() == /datum/reagent/blood && (methods == INJECT || (methods == INGEST && exposed_carbon.dna && exposed_carbon.dna.species && (DRINKSBLOOD in exposed_carbon.dna.species.species_traits))))
if(data && data["blood_type"])
var/datum/blood_type/blood_type = data["blood_type"]
if(blood_type.type in exposed_carbon.dna.blood_type.compatible_types)
exposed_carbon.blood_volume = min(exposed_carbon.blood_volume + round(reac_volume, 0.1), BLOOD_VOLUME_MAXIMUM(L))
return
exposed_carbon.reagents.add_reagent(/datum/reagent/toxin, reac_volume * 0.5)
/datum/reagent/blood/on_new(list/data)
if(istype(data))
SetViruses(src, data)
var/datum/blood_type/blood_type = data["blood_type"]
if(blood_type)
color = blood_type.color
/datum/reagent/blood/on_merge(list/mix_data)
if(data && mix_data)

View File

@@ -4,7 +4,7 @@
icon = 'icons/obj/bloodpack.dmi'
icon_state = "bloodpack"
volume = 200
var/blood_type = null
var/datum/blood_type/blood_type = null
var/unique_blood = null
var/labelled = 0
@@ -65,6 +65,8 @@
/obj/item/reagent_containers/blood/Initialize(mapload)
. = ..()
if(blood_type != null)
if(!istype(blood_type, /datum/blood_type) && get_blood_type(blood_type))
blood_type = get_blood_type(blood_type)
reagents.add_reagent(unique_blood ? unique_blood : /datum/reagent/blood, 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null))
update_appearance(UPDATE_ICON)
@@ -74,7 +76,7 @@
if(B && B.data && B.data["blood_type"])
blood_type = B.data["blood_type"]
else if(reagents.has_reagent(/datum/reagent/consumable/liquidelectricity))
blood_type = "LE"
blood_type = "E"
else
blood_type = null
update_pack_name()
@@ -83,7 +85,7 @@
/obj/item/reagent_containers/blood/proc/update_pack_name()
if(!labelled)
if(blood_type)
name = "blood pack - [blood_type]"
name = "blood pack[blood_type ? " - [unique_blood ? blood_type : blood_type.name]" : null]"
else
name = "blood pack"
@@ -127,12 +129,15 @@
blood_type = "L"
/obj/item/reagent_containers/blood/ethereal
blood_type = "LE"
blood_type = "E"
unique_blood = /datum/reagent/consumable/liquidelectricity
/obj/item/reagent_containers/blood/universal
blood_type = "U"
/obj/item/reagent_containers/blood/gorilla
blood_type = "G"
/obj/item/reagent_containers/blood/attackby(obj/item/I, mob/user, params)
if (istype(I, /obj/item/pen) || istype(I, /obj/item/toy/crayon))
if(!user.is_literate())

View File

@@ -408,7 +408,7 @@ GLOBAL_LIST_EMPTY(bluespace_slime_crystals)
blood_amt = max_blood_amt
break
qdel(B)
else if (istype(B, /obj/effect/decal/cleanable/trail_holder))
else if (istype(B, /obj/effect/decal/cleanable/blood/trail_holder))
blood_amt += 3
if(blood_amt > max_blood_amt)
blood_amt = max_blood_amt

View File

@@ -14,7 +14,8 @@
var/status = BODYPART_ORGANIC
var/sub_status = BODYPART_SUBTYPE_ORGANIC
var/needs_processing = FALSE
///This is effectively the icon_state prefix for limbs.
var/limb_id
var/body_zone //BODY_ZONE_CHEST, BODY_ZONE_L_ARM, etc , used for def_zone
var/aux_zone // used for hands
var/aux_layer
@@ -56,6 +57,10 @@
var/species_color = ""
var/mutation_color = ""
var/no_update = 0
/// The colour of damage done to this bodypart
var/damage_color = ""
/// Should we even use a color?
var/use_damage_color = FALSE
var/animal_origin = null //for nonhuman bodypart (e.g. monkey)
var/dismemberable = 1 //whether it can be dismembered with a weapon.
@@ -848,6 +853,7 @@
body_gender = H.gender
should_draw_gender = S.sexes
use_damage_color = S.use_damage_color
if((MUTCOLORS in S.species_traits) || (DYNCOLORS in S.species_traits))
if(S.fixed_mut_color)
@@ -897,7 +903,10 @@
image_dir = SOUTH
if(dmg_overlay_type)
if(brutestate)
. += image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER, image_dir)
var/image/bruteoverlay = image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER, image_dir)
if(use_damage_color)
bruteoverlay.color = damage_color
. += bruteoverlay
if(burnstate)
. += image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER, image_dir)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

@@ -423,6 +423,7 @@
#include "code\datums\ai_laws.dm"
#include "code\datums\armor.dm"
#include "code\datums\beam.dm"
#include "code\datums\blood_types.dm"
#include "code\datums\browser.dm"
#include "code\datums\callback.dm"
#include "code\datums\chatmessage.dm"
@@ -506,6 +507,7 @@
#include "code\datums\components\bakeable.dm"
#include "code\datums\components\bane.dm"
#include "code\datums\components\beetlejuice.dm"
#include "code\datums\components\bloodysoles.dm"
#include "code\datums\components\butchering.dm"
#include "code\datums\components\caltrop.dm"
#include "code\datums\components\chasm.dm"
@@ -579,7 +581,6 @@
#include "code\datums\components\crafting\recipes.dm"
#include "code\datums\components\crafting\tailoring.dm"
#include "code\datums\components\crafting\weapons.dm"
#include "code\datums\components\decals\blood.dm"
#include "code\datums\components\fantasy\_fantasy.dm"
#include "code\datums\components\fantasy\affix.dm"
#include "code\datums\components\fantasy\prefixes.dm"
@@ -675,6 +676,8 @@
#include "code\datums\elements\rust.dm"
#include "code\datums\elements\squish.dm"
#include "code\datums\elements\update_icon_blocker.dm"
#include "code\datums\elements\decals\_decals.dm"
#include "code\datums\elements\decals\blood.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
#include "code\datums\helper_datums\icon_snapshot.dm"
@@ -2118,8 +2121,8 @@
#include "code\modules\client\preferences\assets.dm"
#include "code\modules\client\preferences\auto_fit_viewport.dm"
#include "code\modules\client\preferences\balloon_alerts.dm"
#include "code\modules\client\preferences\clerk_choice.dm"
#include "code\modules\client\preferences\chapel_choice.dm"
#include "code\modules\client\preferences\clerk_choice.dm"
#include "code\modules\client\preferences\clothing.dm"
#include "code\modules\client\preferences\credits.dm"
#include "code\modules\client\preferences\donor.dm"
@@ -4261,7 +4264,6 @@
#include "yogstation\code\modules\reagents\chemistry\reagents\food_reagents.dm"
#include "yogstation\code\modules\reagents\chemistry\reagents\other_reagents.dm"
#include "yogstation\code\modules\reagents\chemistry\recipes\slime_extracts.dm"
#include "yogstation\code\modules\reagents\reagent_containers\blood_pack.dm"
#include "yogstation\code\modules\reagents\reagent_containers\bottle.dm"
#include "yogstation\code\modules\reagents\reagent_containers\gummies.dm"
#include "yogstation\code\modules\reagents\reagent_containers\hypospray.dm"

View File

@@ -85,7 +85,7 @@
////////////BLOODCRAWL
/datum/component/crawl/blood
crawling_types = list(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/xenoblood, /obj/effect/decal/cleanable/trail_holder)
crawling_types = list(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/xenoblood, /obj/effect/decal/cleanable/blood/trail_holder)
gain_message = span_boldnotice("You can now bloodcrawl! Alt-click blood or gibs to phase in and out.")
loss_message = span_warning("You can no longer bloodcrawl.")

View File

@@ -20,7 +20,7 @@
if (mode)
if (!guardian.Adjacent(target))
return ..()
if (istype(target, /obj/effect/decal/cleanable/blood) || istype(target, /obj/effect/decal/cleanable/trail_holder))
if (istype(target, /obj/effect/decal/cleanable/blood) || istype(target, /obj/effect/decal/cleanable/blood/trail_holder))
guardian.visible_message(span_notice("[guardian] swirls it's finger around in [target] for a bit, before shaking it off."))
var/obj/effect/decal/D = target
var/list/blood = D.return_blood_DNA()

View File

@@ -12,7 +12,7 @@
attack_verb = "assault"
skinned_type = /obj/item/stack/sheet/plasteel{amount = 5} //coated in plasteel
meat = /obj/item/reagent_containers/food/snacks/meat/slab/synthmeat
exotic_bloodtype = "U" //synthetic blood that works for literally everyone
exotic_bloodtype = "Synthetic" //synthetic blood
toxic_food = NONE
liked_food = FRIED | SUGAR | JUNKFOOD
disliked_food = GROSS | VEGETABLES

View File

@@ -1,2 +0,0 @@
/obj/item/reagent_containers/blood/gorilla
blood_type = "G"