Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blood drying optimization #626

Merged
merged 8 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions code/game/objects/effects/decals/cleanable.dm
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,12 @@
return FALSE

bloodiness = clamp((bloodiness + by_amount), 0, BLOOD_POOL_MAX)
update_appearance()
return TRUE

/// Called before attempting to scoop up reagents from this decal to only load reagents when necessary
/obj/effect/decal/cleanable/proc/lazy_init_reagents()
return

#ifdef TESTING
/obj/effect/decal/cleanable/update_overlays()
. = ..()
if(bloodiness)
var/mutable_appearance/blah_text = new()
blah_text.maptext = MAPTEXT_TINY_UNICODE("[bloodiness]")
blah_text.appearance_flags |= (KEEP_APART|RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM)
. += blah_text
#endif

/obj/effect/decal/cleanable/attackby(obj/item/W, mob/user, params)
if((istype(W, /obj/item/reagent_containers/cup) && !istype(W, /obj/item/reagent_containers/cup/rag)) || istype(W, /obj/item/reagent_containers/cup/glass))
if(src.reagents && W.reagents)
Expand Down
131 changes: 84 additions & 47 deletions code/game/objects/effects/decals/cleanable/humans.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
decal_reagent = /datum/reagent/blood
bloodiness = BLOOD_AMOUNT_PER_DECAL
color = COLOR_BLOOD
flags_1 = UNPAINTABLE_1
/// Can this blood dry out?
var/can_dry = TRUE
/// Is this blood dried out?
Expand All @@ -29,21 +30,17 @@
/// How long it takes to dry out
var/drying_time = 5 MINUTES
/// The process to drying out, recorded in deciseconds
var/drying_progress = 0
/// Color matrix applied to dried blood via filter to make it look dried
var/static/list/blood_dry_filter_matrix = list(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
-0.5, -0.75, -0.75, 0,
)
VAR_FINAL/drying_progress = 0

/obj/effect/decal/cleanable/blood/Initialize(mapload)
. = ..()
START_PROCESSING(SSblood_drying, src)
if(color && can_dry && !dried)
update_blood_drying_effect()
if(mapload)
add_blood_DNA(list("UNKNOWN DNA" = random_human_blood_type()))
if(dried)
dry()
else if(can_dry)
START_PROCESSING(SSblood_drying, src)
// update_atom_colour() // this is already called by parent via add_atom_colour

/obj/effect/decal/cleanable/blood/Destroy()
STOP_PROCESSING(SSblood_drying, src)
Expand All @@ -54,29 +51,56 @@
return
return ..()

#define DRY_FILTER_KEY "dry_effect"

/obj/effect/decal/cleanable/blood/proc/update_blood_drying_effect()
if(!can_dry)
remove_filter(DRY_FILTER_KEY) // I GUESS
return

var/existing_filter = get_filter(DRY_FILTER_KEY)
if(dried)
if(existing_filter)
animate(existing_filter) // just stop existing animations and force it to the end state
return
add_filter(DRY_FILTER_KEY, 2, color_matrix_filter(blood_dry_filter_matrix))
// When color changes we need to update the drying animation
/obj/effect/decal/cleanable/blood/update_atom_colour()
. = ..()
// get a default color based on DNA if it ends up unset somehow
color ||= (GET_ATOM_BLOOD_DNA_LENGTH(src) ? get_blood_dna_color() : COLOR_BLOOD)
// stop existing drying animations
animate(src)
// ok let's make the dry color now
// we will manually calculate what the resulting color should be when it dries
// we do this instead of using something like a color matrix because byond moment
// (at any given moment, there may be like... 200 blood decals on your screen at once
// byond is, apparently, pretty bad at handling that many color matrix operations,
// especially in a filter or while animating)
var/list/starting_color_rgb = ReadRGB(color)
// we want a fixed offset for a fixed drop in color intensity, plus a scaling offset based on our strongest color
// the scaling offset helps keep dark colors from turning black, while also ensurse bright colors don't stay super bright
var/max_color = max(starting_color_rgb[1], starting_color_rgb[2], starting_color_rgb[3])
var/red_offset = 50 + (75 * (starting_color_rgb[1] / max_color))
var/green_offset = 50 + (75 * (starting_color_rgb[2] / max_color))
var/blue_offset = 50 + (75 * (starting_color_rgb[3] / max_color))
// if the color is already decently dark, we should reduce the offsets even further
// this is intended to prevent already dark blood (mixed blood in particular) from becoming full black
var/strength = starting_color_rgb[1] + starting_color_rgb[2] + starting_color_rgb[3]
if(strength <= 192)
red_offset *= 0.5
green_offset *= 0.5
blue_offset *= 0.5
// finally, get this show on the road
var/dried_color = rgb(
clamp(starting_color_rgb[1] - red_offset, 0, 255),
clamp(starting_color_rgb[2] - green_offset, 0, 255),
clamp(starting_color_rgb[3] - blue_offset, 0, 255),
length(starting_color_rgb) >= 4 ? starting_color_rgb[4] : 255, // maintain alpha! (if it has it)
)
// if it's dried (or about to dry) we can just set color directly
if(dried || drying_progress >= drying_time)
color = dried_color
return

if(existing_filter)
remove_filter(DRY_FILTER_KEY)

add_filter(DRY_FILTER_KEY, 2, color_matrix_filter())
transition_filter(DRY_FILTER_KEY, color_matrix_filter(blood_dry_filter_matrix), drying_time - drying_progress)

#undef DRY_FILTER_KEY

// otherwise set the color to what it should be at the current drying progress, then animate down to the dried color if we can
color = gradient(0, color, 1, dried_color, round(drying_progress / drying_time, 0.01))
if(can_dry)
animate(src, time = drying_time - drying_progress, color = dried_color)

/// Slows down the drying time by a given amount,
/// then updates the effect, meaning the animation will slow down
/obj/effect/decal/cleanable/blood/proc/slow_dry(by_amount)
drying_progress -= by_amount
update_atom_colour()

/// Returns a string of all the blood reagents in the blood
/obj/effect/decal/cleanable/blood/proc/get_blood_string()
var/list/all_dna = GET_ATOM_BLOOD_DNA(src)
var/list/all_blood_names = list()
Expand All @@ -91,7 +115,8 @@

adjust_bloodiness(-0.4 * BLOOD_PER_UNIT_MODIFIER * seconds_per_tick)
drying_progress += (seconds_per_tick * 1 SECONDS)
if(drying_progress >= drying_time + SSblood_drying.wait) // Do it next tick when we're done
// Finish it next tick when we're all done
if(drying_progress >= drying_time + SSblood_drying.wait)
dry()

/obj/effect/decal/cleanable/blood/update_name(updates)
Expand Down Expand Up @@ -121,7 +146,7 @@
dried = TRUE
reagents?.clear_reagents()
update_appearance()
update_blood_drying_effect()
update_atom_colour()
STOP_PROCESSING(SSblood_drying, src)
return TRUE

Expand All @@ -145,17 +170,12 @@
. = ..()
merger.add_blood_DNA(GET_ATOM_BLOOD_DNA(src))
merger.adjust_bloodiness(bloodiness)
merger.drying_progress -= (bloodiness * BLOOD_PER_UNIT_MODIFIER) // goes negative = takes longer to dry
merger.update_blood_drying_effect()
merger.slow_dry(1 SECONDS * bloodiness * BLOOD_PER_UNIT_MODIFIER)

/obj/effect/decal/cleanable/blood/old
bloodiness = 0
icon_state = "floor1-old"

/obj/effect/decal/cleanable/blood/old/Initialize(mapload, list/datum/disease/diseases)
add_blood_DNA(list("UNKNOWN DNA" = random_human_blood_type()))
. = ..()
dry()
dried = TRUE
icon_state = "floor1-old" // just for mappers. overrided in init

/obj/effect/decal/cleanable/blood/splatter
icon_state = "gibbl1"
Expand Down Expand Up @@ -297,17 +317,16 @@
/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"
icon_state = "gib1-old" // just for mappers. overrided in init
bloodiness = 0
dried = TRUE
dry_prefix = ""
dry_desc = ""

/obj/effect/decal/cleanable/blood/gibs/old/Initialize(mapload, list/datum/disease/diseases)
add_blood_DNA(list("UNKNOWN DNA" = random_human_blood_type()))
. = ..()
setDir(pick(GLOB.cardinals))
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLUDGE, CELL_VIRUS_TABLE_GENERIC, rand(2,4), 10)
dry()

/obj/effect/decal/cleanable/blood/drip
name = "drips of blood"
Expand Down Expand Up @@ -538,3 +557,21 @@
the_window.vis_contents += final_splatter
the_window.bloodied = TRUE
expire()

/// Subtype which has random DNA baked in OUTSIDE of mapload.
/// For testing, mapping, or badmins
/obj/effect/decal/cleanable/blood/pre_dna
var/list/dna_types = list("UNKNOWN DNA A" = /datum/blood_type/crew/human/a_minus)

/obj/effect/decal/cleanable/blood/pre_dna/Initialize(mapload)
. = ..()
add_blood_DNA(dna_types)

/obj/effect/decal/cleanable/blood/pre_dna/lizard
dna_types = list("UNKNOWN DNA A" = /datum/blood_type/crew/lizard)

/obj/effect/decal/cleanable/blood/pre_dna/lizhuman
dna_types = list("UNKNOWN DNA A" = /datum/blood_type/crew/human/a_minus, "UNKNOWN DNA B" = /datum/blood_type/crew/lizard)

/obj/effect/decal/cleanable/blood/pre_dna/ethereal
dna_types = list("UNKNOWN DNA A" = /datum/blood_type/crew/ethereal)
9 changes: 7 additions & 2 deletions code/modules/forensics/forensics_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
/// Cached mixed color of all blood DNA on us
VAR_PROTECTED/cached_blood_dna_color

/// Gets what color all the blood coating this atom mixed together would be
/atom/proc/get_blood_dna_color()
if(cached_blood_dna_color)
return cached_blood_dna_color
Expand All @@ -92,6 +93,9 @@
cached_blood_dna_color = final_color
return final_color

/obj/effect/decal/cleanable/blood/get_blood_dna_color()
return ..() || COLOR_BLOOD

/obj/effect/decal/cleanable/blood/drip/get_blood_dna_color()
var/list/all_dna = GET_ATOM_BLOOD_DNA(src)
var/blood_type_to_use = all_dna[all_dna[1]]
Expand All @@ -117,13 +121,14 @@
var/first_dna = GET_ATOM_BLOOD_DNA_LENGTH(src)
if(!..())
return FALSE

color = get_blood_dna_color()
if(dried)
return TRUE
// Imperfect, ends up with some blood types being double-set-up, but harmless (for now)
for(var/new_blood in blood_DNA_to_add)
var/datum/blood_type/blood = find_blood_type(blood_DNA_to_add[new_blood])
blood.set_up_blood(src, first_dna == 0)
update_appearance()
add_atom_colour(get_blood_dna_color(), FIXED_COLOUR_PRIORITY)
return TRUE

/obj/item/add_blood_DNA(list/blood_DNA_to_add)
Expand Down
18 changes: 8 additions & 10 deletions maplestation_modules/code/modules/mob/living/blood.dm
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@
* Blood Drying SS
*
* Used as a low priority backround system to handling the drying of blood on the ground
* (basically just handles reducing their bloodiness value over time)
*/
PROCESSING_SUBSYSTEM_DEF(blood_drying)
name = "Blood Drying"
flags = SS_NO_INIT | SS_BACKGROUND
priority = 10
wait = 10 SECONDS
wait = 4 SECONDS

/**
* Blood Types
Expand Down Expand Up @@ -101,6 +102,9 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying)
/**
* Used to handle any unique facets of blood spawned of this blood type
*
* You don't need to worry about updating the icon of the decal,
* it will be handled automatically after setup is finished
*
* Arguments
* * blood - the blood being set up
* * new_splat - whether this is a newly instantiated blood decal, or an existing one this blood is being added to
Expand Down Expand Up @@ -132,10 +136,9 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying)
if(isnull(drop))
var/obj/effect/decal/cleanable/blood/splatter = locate() in blood_turf
if(!QDELETED(splatter))
splatter.adjust_bloodiness(new_blood)
splatter.drying_progress -= (new_blood * BLOOD_PER_UNIT_MODIFIER)
splatter.update_blood_drying_effect()
splatter.add_mob_blood(bleeding)
splatter.adjust_bloodiness(new_blood)
splatter.slow_dry(1 SECONDS * new_blood * BLOOD_PER_UNIT_MODIFIER)
return splatter

drop = new(blood_turf, bleeding.get_static_viruses())
Expand Down Expand Up @@ -170,8 +173,7 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying)
return null
else
splatter.adjust_bloodiness(BLOOD_AMOUNT_PER_DECAL)
splatter.drying_progress -= (BLOOD_AMOUNT_PER_DECAL * BLOOD_PER_UNIT_MODIFIER)
splatter.update_blood_drying_effect()
splatter.slow_dry(1 SECONDS * BLOOD_AMOUNT_PER_DECAL * BLOOD_PER_UNIT_MODIFIER)
splatter.add_mob_blood(bleeding) //give blood info to the blood decal.
if(LAZYLEN(temp_blood_DNA))
splatter.add_blood_DNA(temp_blood_DNA)
Expand Down Expand Up @@ -286,7 +288,6 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying)
/datum/blood_type/crew/lizard/silver/set_up_blood(obj/effect/decal/cleanable/blood/blood, new_splat)
blood.add_filter("silver_glint", 3, list("type" = "outline", "color" = "#c9c9c99c", "size" = 1.5))
blood.emissive_alpha = max(blood.emissive_alpha, new_splat ? 125 : 63)
blood.update_appearance(UPDATE_OVERLAYS)

/datum/blood_type/crew/skrell
name = "S"
Expand All @@ -300,11 +301,9 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying)

/datum/blood_type/crew/ethereal/set_up_blood(obj/effect/decal/cleanable/blood/blood, new_splat)
blood.emissive_alpha = max(blood.emissive_alpha, new_splat ? 188 : 125)
blood.update_appearance(UPDATE_OVERLAYS)
if(!new_splat)
return
blood.can_dry = FALSE
blood.update_blood_drying_effect()
RegisterSignals(blood, list(COMSIG_ATOM_ITEM_INTERACTION, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY), PROC_REF(on_cleaned))

/datum/blood_type/crew/ethereal/proc/on_cleaned(obj/effect/decal/cleanable/source, mob/living/user, obj/item/tool, ...)
Expand Down Expand Up @@ -337,7 +336,6 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying)
return
// Oil blood will never dry and can be ignited with fire
blood.can_dry = FALSE
blood.update_blood_drying_effect()
blood.AddElement(/datum/element/easy_ignite)

/// A universal blood type which accepts everything
Expand Down
Loading