diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index c00ad525b181..19d437443c7a 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -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) diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 79144056e6ad..53bf88a1659e 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -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? @@ -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) @@ -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() @@ -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) @@ -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 @@ -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" @@ -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" @@ -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) diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm index f11ddea8c4f2..c4008e9f21f3 100644 --- a/code/modules/forensics/forensics_helpers.dm +++ b/code/modules/forensics/forensics_helpers.dm @@ -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 @@ -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]] @@ -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) diff --git a/maplestation_modules/code/modules/mob/living/blood.dm b/maplestation_modules/code/modules/mob/living/blood.dm index 62b31fb0cc4a..87278eb81250 100644 --- a/maplestation_modules/code/modules/mob/living/blood.dm +++ b/maplestation_modules/code/modules/mob/living/blood.dm @@ -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 @@ -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 @@ -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()) @@ -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) @@ -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" @@ -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, ...) @@ -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