The full-custom blood colour quirk, again (#4332)

## About The Pull Request
Technically, this is also a bugfix for the removed portion of the evil
quirk.

This is a blood colour quirk which aims to create an entirely new
blood_type datum per color-modified base type, and acts as a launching
pad into developing more blood stuff.

As an additional note: In our testing injected blood wouldn't override
the recipient's blood colour. However if this does happen at all in
live, do state such!

## Why It's Good For The Game

Blood colour good? Near-fully custom blood color better?

## Proof Of Testing
oh hey it works now. The three lizards in the video use red blood,
cyan/vox blood, and white/nanite blood respectively.

https://github.com/user-attachments/assets/cec6465f-68de-4c6f-97f4-58061f4f7241

Not rigorously tested, but my process was to only dev the lizard type
then copy to everything else... so it should be fine? Just to show it
should work on blood types with existing compatible_type fields though -
as in, it actually copies the list:
<img width="491" height="260" alt="Screenshot_92"
src="https://github.com/user-attachments/assets/9c257d44-1233-4276-9d88-599e124b2838"
/>

## What We Learnt
Trust our gut. We shouldve replaced it ages ago but thought original
refactorer knew better

## Changelog

🆑
add: blood colour quirk. it's mechanically near-identical to the normal
type.
code: more blood colour presets. yum.

---------

Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
This commit is contained in:
aKromatopzia
2025-11-20 12:46:06 -05:00
committed by GitHub
co-authored by LT3 Arturlang
parent 3f0e8956b5
commit 48dc5f593d
8 changed files with 444 additions and 0 deletions
@@ -9,8 +9,15 @@ GLOBAL_LIST_INIT(chat_colors_by_mob_name, list(
"Unknown" = list("#ffffff", "#d8d8d8"),
))
/// Blood colors
#define BLOOD_COLOR_COPPER "#6E3B08"
#define BLOOD_COLOR_NANITE_SLURRY "#CCCCCC"
//alternate lizard blood
#define BLOOD_COLOR_GREEN "#339933"
//old-canon avalis had violet blood, so...
#define BLOOD_COLOR_VIOLET "#9966FF"
//cyan vox blood
#define BLOOD_COLOR_CYAN "#33FFFF"
/// Modsuit hardlight colors
@@ -110,3 +110,16 @@ GLOBAL_LIST_INIT(color_list_ghoul, list( \
"Plutonium Blue" = "a5cfcc", \
"Marked Red" = "f05b68" \
))
//blood colours
GLOBAL_LIST_INIT(custom_blood_colors, list(
"Red/Human" = BLOOD_COLOR_RED,
"D.Green/Lizard" = BLOOD_COLOR_LIZARD,
"Green" = BLOOD_COLOR_GREEN,
"Lime/Xeno" = BLOOD_COLOR_XENO,
"Violet/Avali" = BLOOD_COLOR_VIOLET,
"Cyan/Vox" = BLOOD_COLOR_CYAN,
"Brown/Skrell" = BLOOD_COLOR_COPPER,
"White/Nanites" = BLOOD_COLOR_NANITE_SLURRY,
"Black" = BLOOD_COLOR_BLACK,
))
@@ -0,0 +1,41 @@
///see modular_zubbers\code\modules\quirks\neutral_quirks\unique_blood_color.dm
///manual colour input
/datum/preference/color/input_blood_color
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "input_blood_color"
savefile_identifier = PREFERENCE_CHARACTER
/datum/preference/color/input_blood_color/create_default_value()
return BLOOD_COLOR_BLACK
/datum/preference/color/input_blood_color/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
return /datum/quirk/item_quirk/unique_blood_color::name in preferences.all_quirks
/datum/preference/color/input_blood_color/apply_to_human(mob/living/carbon/human/target, value)
return
///colour preset selection
/datum/preference/choiced/select_blood_color
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "select_blood_color"
savefile_identifier = PREFERENCE_CHARACTER
can_randomize = FALSE
/datum/preference/choiced/select_blood_color/create_default_value()
return "Custom"
/datum/preference/choiced/select_blood_color/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
return /datum/quirk/item_quirk/unique_blood_color::name in preferences.all_quirks
/datum/preference/choiced/select_blood_color/apply_to_human(mob/living/carbon/human/target, value)
return
/datum/preference/choiced/select_blood_color/init_possible_values()
return list("Custom") + assoc_to_keys(GLOB.custom_blood_colors)
@@ -0,0 +1,10 @@
/obj/item/clothing/accessory/dogtag/unique_blood
name = "Blood dogtag"
desc = "A dogtag with a listing of blood properties."
/obj/item/clothing/accessory/dogtag/unique_blood/Initialize(mapload, blood_type, color_string)
. = ..()
if(color_string && blood_type)
display = span_notice("\"Hi! My blood is [blood_type] despite being [color_string]!\"")
else
display = span_notice("The dogtag is all scratched up.")
@@ -1,3 +1,15 @@
/datum/blood_type
///blood type to be edited for change_blood_color
var/recolor_blood_type //datum typepath for the alt_color version of the blood type
/datum/blood_type/proc/make_alt_color(override, datum/blood_type/orig)
var/datum/blood_type/original = orig
name = "[name] ([override])"
color = override
id = name
testing("created alternate [id]")
compatible_types = LAZYLISTDUPLICATE(original?.compatible_types)
// For Skrell
/datum/blood_type/copper
name = BLOOD_TYPE_COPPER
@@ -5,6 +17,16 @@
reagent_type = /datum/reagent/copper
color = BLOOD_COLOR_COPPER
restoration_chem = null
recolor_blood_type = /datum/blood_type/copper/alt_color
/datum/blood_type/copper/alt_color
abstract_type = /datum/blood_type/copper
/datum/blood_type/copper/alt_color/type_key()
return /datum/blood_type/copper
/datum/blood_type/copper/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
// For Proteans
/datum/blood_type/nanite_slurry
@@ -13,3 +35,261 @@
reagent_type = /datum/reagent/medicine/nanite_slurry
color = BLOOD_COLOR_NANITE_SLURRY
restoration_chem = null
recolor_blood_type = /datum/blood_type/nanite_slurry/alt_color
/datum/blood_type/nanite_slurry/alt_color
abstract_type = /datum/blood_type/nanite_slurry
/datum/blood_type/nanite_slurry/alt_color/type_key()
return /datum/blood_type/nanite_slurry
/datum/blood_type/nanite_slurry/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/* blood type template for alt_colors
/datum/blood_type/namehere
recolor_blood_type = /datum/blood_type/namehere/alt_color
/datum/blood_type/namehere/alt_color
abstract_type = /datum/blood_type/namehere/alt_color
/datum/blood_type/namehere/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/alt_color/type_key()
return /datum/blood_type/PARENT_TYPE_HERE
*/
///A-
/datum/blood_type/human/a_minus
recolor_blood_type = /datum/blood_type/human/a_minus/alt_color
/datum/blood_type/human/a_minus/alt_color
/* abstract_type = /datum/blood_type/human/a_minus/alt_color */
/datum/blood_type/human/a_minus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/a_minus/alt_color/type_key()
return /datum/blood_type/human/a_minus
///A+
/datum/blood_type/human/a_plus
recolor_blood_type = /datum/blood_type/human/a_plus/alt_color
/datum/blood_type/human/a_plus/alt_color
/* abstract_type = /datum/blood_type/human/a_plus/alt_color */
/datum/blood_type/human/a_plus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/a_plus/alt_color/type_key()
return /datum/blood_type/human/a_plus
///B+
/datum/blood_type/human/b_plus
recolor_blood_type = /datum/blood_type/human/b_plus/alt_color
/datum/blood_type/human/b_plus/alt_color
/* abstract_type = /datum/blood_type/human/b_plus/alt_color */
/datum/blood_type/human/b_plus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/b_plus/alt_color/type_key()
return /datum/blood_type/human/b_plus
///B-
/datum/blood_type/human/b_minus
recolor_blood_type = /datum/blood_type/human/b_minus/alt_color
/datum/blood_type/human/b_minus/alt_color
/* abstract_type = /datum/blood_type/human/b_minus/alt_color */
/datum/blood_type/human/b_minus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/b_minus/alt_color/type_key()
return /datum/blood_type/human/b_minus
///AB-
/datum/blood_type/human/ab_minus
recolor_blood_type = /datum/blood_type/human/ab_minus/alt_color
/datum/blood_type/human/ab_minus/alt_color
/* abstract_type = /datum/blood_type/human/ab_minus/alt_color */
/datum/blood_type/human/ab_minus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/ab_minus/alt_color/type_key()
return /datum/blood_type/human/ab_minus
///AB+
/datum/blood_type/human/ab_plus
recolor_blood_type = /datum/blood_type/human/ab_plus/alt_color
/datum/blood_type/human/ab_plus/alt_color
/* abstract_type = /datum/blood_type/human/ab_plus/alt_color */
/datum/blood_type/human/ab_plus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/ab_plus/alt_color/type_key()
return /datum/blood_type/human/ab_plus
///O-
/datum/blood_type/human/o_minus
recolor_blood_type = /datum/blood_type/human/o_minus/alt_color
/datum/blood_type/human/o_minus/alt_color
/* abstract_type = /datum/blood_type/human/o_minus/alt_color */
/datum/blood_type/human/o_minus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/o_minus/alt_color/type_key()
return /datum/blood_type/human/o_minus
///O+
/datum/blood_type/human/o_plus
recolor_blood_type = /datum/blood_type/human/o_plus/alt_color
/datum/blood_type/human/o_plus/alt_color
/* abstract_type = /datum/blood_type/human/o_plus/alt_color */
/datum/blood_type/human/o_plus/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/human/o_plus/alt_color/type_key()
return /datum/blood_type/human/o_plus
///Y-
/datum/blood_type/animal
recolor_blood_type = /datum/blood_type/animal/alt_color
/datum/blood_type/animal/alt_color
abstract_type = /datum/blood_type/animal/alt_color
/datum/blood_type/animal/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/animal/alt_color/type_key()
return /datum/blood_type/animal
///L
/datum/blood_type/lizard
recolor_blood_type = /datum/blood_type/lizard/alt_color
/datum/blood_type/lizard/alt_color
abstract_type = /datum/blood_type/lizard/alt_color
/datum/blood_type/lizard/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/lizard/alt_color/type_key()
return /datum/blood_type/lizard
///LE
/datum/blood_type/ethereal
recolor_blood_type = /datum/blood_type/ethereal/alt_color
/datum/blood_type/ethereal/alt_color
abstract_type = /datum/blood_type/ethereal/alt_color
/datum/blood_type/ethereal/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/ethereal/alt_color/type_key()
return /datum/blood_type/ethereal
///Oil
/datum/blood_type/oil
recolor_blood_type = /datum/blood_type/oil/alt_color
/datum/blood_type/oil/alt_color
abstract_type = /datum/blood_type/oil/alt_color
/datum/blood_type/oil/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/oil/alt_color/type_key()
return /datum/blood_type/oil
///V
/datum/blood_type/vampire
recolor_blood_type = /datum/blood_type/vampire/alt_color
/datum/blood_type/vampire/alt_color
abstract_type = /datum/blood_type/vampire/alt_color
/datum/blood_type/vampire/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/vampire/alt_color/type_key()
return /datum/blood_type/vampire
///U
/datum/blood_type/universal
recolor_blood_type = /datum/blood_type/universal/alt_color
/datum/blood_type/universal/alt_color
abstract_type = /datum/blood_type/universal/alt_color
/datum/blood_type/universal/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/universal/alt_color/type_key()
return /datum/blood_type/universal
///MT-
/datum/blood_type/meat
recolor_blood_type = /datum/blood_type/meat/alt_color
/datum/blood_type/meat/alt_color
abstract_type = /datum/blood_type/meat/alt_color
/datum/blood_type/meat/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/meat/alt_color/type_key()
return /datum/blood_type/meat
///TOX
/datum/blood_type/slime
recolor_blood_type = /datum/blood_type/slime/alt_color
/datum/blood_type/slime/alt_color
abstract_type = /datum/blood_type/slime/alt_color
/datum/blood_type/slime/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/slime/alt_color/type_key()
return /datum/blood_type/slime
///H2O
/datum/blood_type/water
recolor_blood_type = /datum/blood_type/water/alt_color
/datum/blood_type/water/alt_color
abstract_type = /datum/blood_type/water/alt_color
/datum/blood_type/water/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/water/alt_color/type_key()
return /datum/blood_type/water
///S
/datum/blood_type/snail
recolor_blood_type = /datum/blood_type/snail/alt_color
/datum/blood_type/snail/alt_color
abstract_type = /datum/blood_type/snail/alt_color
/datum/blood_type/snail/alt_color/New(override, datum/blood_type/orig)
make_alt_color(override, orig)
/datum/blood_type/snail/alt_color/type_key()
return /datum/blood_type/snail
@@ -0,0 +1,75 @@
//i dont enjoy this implementation, but we're also not going to figure out how to write a proper do-all replace_blood_type() without |some| help....
/datum/quirk/item_quirk/unique_blood_color
name = "Blood Pigmentation"
desc = "Your blood has a different colour than what your species usually has."
gain_text = span_purple("Your flesh shifts in hue.")
lose_text = span_purple("Your flesh returns to a natural look.")
medical_record_text = "Patient has unusual blood pigmentation."
value = 0
icon = FA_ICON_DROPLET
//this should only ever be a hex
var/color_code = NONE
var/datum/blood_type/new_blood = NONE
//for removal, and having an un-appended type name
var/datum/blood_type/old_blood = NONE
/datum/quirk_constant_data/uniquebloodcolor
associated_typepath = /datum/quirk/item_quirk/unique_blood_color
customization_options = list(
/datum/preference/color/input_blood_color,
/datum/preference/choiced/select_blood_color,
)
/datum/quirk/item_quirk/unique_blood_color/add(client/client_source)
var/mob/living/carbon/human/human_holder = quirk_holder
//If you want to change the blood of a non-human mob, just varedit. It'll reset when changing species tho
if(!istype(human_holder))
return
old_blood = human_holder.dna.blood_type
var/selected_color = client_source?.prefs.read_preference(/datum/preference/choiced/select_blood_color) || "Custom"
testing("selected_color is [selected_color]")
if(selected_color != "Custom")
color_code = GLOB.custom_blood_colors[selected_color]
testing("selected_color is not Custom")
else if(selected_color == "Custom")
color_code = client_source?.prefs.read_preference(/datum/preference/color/input_blood_color)
testing("selected_color is Custom")
testing("color_code set to [color_code]")
change_blood_color(quirked = human_holder, override = color_code)
/datum/quirk/item_quirk/unique_blood_color/remove()
var/mob/living/carbon/human/human_holder = quirk_holder
if (!istype(human_holder) || isnull(old_blood))
return
var/datum/blood_type/new_blood = new old_blood.type
human_holder.set_blood_type(new_blood)
human_holder.dna.species.exotic_bloodtype = initial(human_holder.dna.species.exotic_bloodtype)
//someone could turn this into a universal replace_blood() or something... but not me
/datum/quirk/item_quirk/unique_blood_color/proc/change_blood_color(datum/source, mob/living/carbon/human/quirked, override, update_cached_blood_dna_info)
SIGNAL_HANDLER
///Check if new blood type is same as old. Jank, but it should prevent cases of special blood types made from relative normal blood
if(quirked.dna.blood_type.get_color() == color_code)
to_chat(quirked, span_notice("...but nothing had actually changed."))
remove_from_current_holder()
WARNING("quirk 'unique_blood_color' automatically removed from [quirked.name]")
return
///Making the new blood type
new_blood = get_blood_type("[quirked.dna.blood_type] ([override])") //for example, A-_#69af19
///check if blood type already exists before making it new
if(isnull(new_blood))
var/recolor_type = quirked.dna.blood_type.recolor_blood_type
new_blood = new recolor_type(override = override, orig = quirked.dna.blood_type)
GLOB.blood_types[new_blood.id] = new_blood
NOTICE("finalized new blood type [new_blood.id] for [quirked.name]")
quirked.dna.species.exotic_bloodtype = new_blood
quirked.set_blood_type(new_blood)
/datum/quirk/item_quirk/unique_blood_color/add_unique(client/client_source)
var/get_bloodtype_id = old_blood.name
var/obj/item/clothing/accessory/dogtag/unique_blood/dogtag = new(quirk_holder, get_bloodtype_id, color_code)
give_item_to_holder(dogtag, list(LOCATION_BACKPACK, LOCATION_HANDS), flavour_text = "Medical should probably see this...", notify_player = TRUE)
+3
View File
@@ -9218,6 +9218,7 @@
#include "modular_zubbers\code\modules\client\preferences\permanent_limp.dm"
#include "modular_zubbers\code\modules\client\preferences\player_panel.dm"
#include "modular_zubbers\code\modules\client\preferences\sounds.dm"
#include "modular_zubbers\code\modules\client\preferences\unique_blood_color.dm"
#include "modular_zubbers\code\modules\client\verbs\character_directory.dm"
#include "modular_zubbers\code\modules\clothing\_job.dm"
#include "modular_zubbers\code\modules\clothing\donator_clothing.dm"
@@ -9261,6 +9262,7 @@
#include "modular_zubbers\code\modules\clothing\under\security.dm"
#include "modular_zubbers\code\modules\clothing\under\skirts_dresses.dm"
#include "modular_zubbers\code\modules\clothing\under\syndicate.dm"
#include "modular_zubbers\code\modules\clothing\under\accessories\badges.dm"
#include "modular_zubbers\code\modules\clothing\under\jobs\command.dm"
#include "modular_zubbers\code\modules\colony_fabricator\code\design_datums\fabricator_flag_additions\tools.dm"
#include "modular_zubbers\code\modules\contractor\code\items\boxes.dm"
@@ -9609,6 +9611,7 @@
#include "modular_zubbers\code\modules\protected_roles\code\antag_restricted_jobs.dm"
#include "modular_zubbers\code\modules\public_logging\public_logging.dm"
#include "modular_zubbers\code\modules\quirks\neutral_quirks\body_temp.dm"
#include "modular_zubbers\code\modules\quirks\neutral_quirks\unique_blood_color.dm"
#include "modular_zubbers\code\modules\quirks\positive_quirks\telepathy.dm"
#include "modular_zubbers\code\modules\quote_of_the_round\config.dm"
#include "modular_zubbers\code\modules\quote_of_the_round\debug_verbs.dm"
@@ -0,0 +1,15 @@
import { Feature, FeatureChoiced, FeatureColorInput } from '../../base';
import { FeatureDropdownInput } from '../../dropdowns';
export const input_blood_color: Feature<string> = {
name: 'Custom color',
description:
'NOTE: This matches the colour to the BRIGHTEST px. So, make this slightly (about 10?) more luminescent (HSL) than you want the blood to be.',
component: FeatureColorInput,
};
export const select_blood_color: FeatureChoiced = {
name: 'Preset color',
description: 'NOTE: Use the Custom option for custom color.',
component: FeatureDropdownInput,
};