diff --git a/code/__DEFINES/color.dm b/code/__DEFINES/color.dm index 62f53d39671..e5133a34a99 100644 --- a/code/__DEFINES/color.dm +++ b/code/__DEFINES/color.dm @@ -114,8 +114,11 @@ // Blood colors #define COLOR_HUMAN_BLOOD "#A10808" -#define COLOR_DIONA_BLOOD "#97DD7C" +/// for robots with black oil "blood" #define COLOR_IPC_BLOOD "#1F181F" +/// for organics with synthetic blood substitute +#define COLOR_SYNTH_BLOOD "#D9F9FF" +#define COLOR_DIONA_BLOOD "#97DD7C" #define COLOR_SKRELL_BLOOD "#0081CD" #define COLOR_VAURCA_BLOOD "#E6E600" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index bcdf9dfb2ae..bdc7d93d5b5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -416,7 +416,7 @@ M.dna.real_name = M.real_name M.check_dna() if (M.species) - blood_color = M.species.blood_color + blood_color = M.get_blood_color() . = 1 return 1 diff --git a/code/game/gamemodes/cult/cultist_datum.dm b/code/game/gamemodes/cult/cultist_datum.dm index fd1ebee7b05..d1e6df07632 100644 --- a/code/game/gamemodes/cult/cultist_datum.dm +++ b/code/game/gamemodes/cult/cultist_datum.dm @@ -119,5 +119,5 @@ to_chat(scribe, SPAN_CULT("You can only draw [runes_allowed] more of this rune.")) R.blood_DNA = list() R.blood_DNA[scribe.dna.unique_enzymes] = scribe.dna.b_type - R.color = scribe.species.blood_color - R.filters = filter(type="drop_shadow", x = 1, y = 1, size = 4, color = scribe.species.blood_color) + R.color = scribe.get_blood_color() + R.filters = filter(type="drop_shadow", x = 1, y = 1, size = 4, color = scribe.get_blood_color()) diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 13a358d4a41..ac54d4d0121 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -1,4 +1,8 @@ -var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-") +var/global/list/valid_bloodtypes = list( + "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-", + "SBS" // Synthetic Blood Substitute. Intended for heavily augmented characters. + // Sanitized below, removed if character is not augmented enough. +) /datum/preferences var/equip_preview_mob = EQUIP_PREVIEW_ALL @@ -217,6 +221,11 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O if(!pref.bgstate || !(pref.bgstate in list_values(pref.bgstate_options))) pref.bgstate = "plain_black" + // Synthetic Blood Substitute checks. + if((pref.b_type == "SBS") && ((length(pref.organ_data) + length(pref.rlimb_data)) < 8)) + to_chat(pref.client, SPAN_WARNING("Synthetic Blood Substitute (SBS) is intended for heavily augmented characters. To pick it, you must have at least eight augmented limbs or organs. Resetting blood type...")) + pref.b_type = initial(pref.b_type) + /datum/category_item/player_setup_item/general/body/content(var/mob/user) var/list/out = list() diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 560da8729f5..316cd7b2934 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -978,6 +978,11 @@ desc = "An embroidered patch indicating the wearer's blood type as AB NEGATIVE." icon_state = "abnegtag" +/obj/item/clothing/accessory/blood_patch/sbs + name = "SBS blood patch" + desc = "An embroidered patch indicating the wearer's blood type as SYNTHETIC BLOOD SUBSTITUTE." + icon_state = "sbstag" + // Corporate Liaison stuff. diff --git a/code/modules/hallucinations/types/mirage.dm b/code/modules/hallucinations/types/mirage.dm index 1aad15ca299..5820b582957 100644 --- a/code/modules/hallucinations/types/mirage.dm +++ b/code/modules/hallucinations/types/mirage.dm @@ -56,7 +56,7 @@ else var/icon/T = new('icons/effects/drip.dmi') I = image(T, pick(T.IconStates()), layer = TURF_LAYER) - I.color = holder.species?.blood_color + I.color = holder.get_blood_color() return I diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 1484523787e..c30ca732347 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -14,7 +14,7 @@ I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)), rand(1,3), round(30/I.w_class)) ..(species.gibbed_anim) - gibs(loc, viruses, dna, null, species.flesh_color, species.blood_color) + gibs(loc, viruses, dna, null, species.flesh_color, get_blood_color()) /mob/living/carbon/human/dust() vr_disconnect() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 525f26d556d..80e42ae4f5b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1340,7 +1340,7 @@ var/mob/living/carbon/human/H = C if(!blood_DNA[H.dna.unique_enzymes]) blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - hand_blood_color = H.species?.blood_color + hand_blood_color = H.get_blood_color() src.update_inv_gloves() //handles bloody hands overlays and updating add_verb(src, /mob/living/carbon/human/proc/bloody_doodle) return TRUE //we applied blood to the item diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 690cb3dbd21..f0083a39742 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -943,13 +943,13 @@ // Add wound overlays for(var/obj/item/organ/external/O in organs) if(O.damage_state == "00") continue - var/cache_index = "[O.damage_state]/[O.icon_name]/[species.blood_color]/[species.get_bodytype()]" + var/cache_index = "[O.damage_state]/[O.icon_name]/[get_blood_color()]/[species.get_bodytype()]" var/list/damage_icon_parts = SSicon_cache.damage_icon_parts var/icon/DI = damage_icon_parts[cache_index] if(!DI) DI = new /icon(species.damage_overlays, O.damage_state) // the damage icon for whole human DI.Blend(new /icon(species.damage_mask, O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels - DI.Blend(species.blood_color, ICON_MULTIPLY) + DI.Blend(get_blood_color(), ICON_MULTIPLY) damage_icon_parts[cache_index] = DI health_images += DI if(O.is_stump()) diff --git a/code/modules/mob/living/carbon/human/species/outsider/undead.dm b/code/modules/mob/living/carbon/human/species/outsider/undead.dm index 05d538163c2..07a24adf804 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/undead.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/undead.dm @@ -537,7 +537,7 @@ nutrition += 40 playsound(loc, 'sound/effects/splat.ogg', 20, 1) - new /obj/effect/decal/cleanable/blood/splatter(get_turf(src), target.species.blood_color) + new /obj/effect/decal/cleanable/blood/splatter(get_turf(src), target.get_blood_color()) if (target.getBruteLoss() > target.maxHealth*0.75) if (prob(50)) gibs(get_turf(src), target.dna) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 1394dd205ec..4dde03f0fff 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -230,13 +230,13 @@ There are several things that need to be remembered: O.update_icon() if(O.damage_state == "00") continue - var/cache_index = "[O.damage_state]/[O.icon_name]/[species.blood_color]/[GET_BODY_TYPE]" + var/cache_index = "[O.damage_state]/[O.icon_name]/[get_blood_color()]/[GET_BODY_TYPE]" var/list/damage_icon_parts = SSicon_cache.damage_icon_parts var/icon/DI = damage_icon_parts[cache_index] if(!DI) DI = new /icon(species.damage_overlays, O.damage_state) // the damage icon for whole human DI.Blend(new /icon(species.damage_mask, O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels - DI.Blend(species.blood_color, ICON_MULTIPLY) + DI.Blend(get_blood_color(), ICON_MULTIPLY) damage_icon_parts[cache_index] = DI LAZYADD(ovr, DI) @@ -1489,7 +1489,7 @@ There are several things that need to be remembered: overlay_state = "[base_state]-blood" if(overlay_state in surgery_states) var/image/blood = image(icon = surgery_icon, icon_state = overlay_state, layer = -SURGERY_LAYER) - blood.color = E.owner.species.blood_color + blood.color = E.owner.get_blood_color() blood.appearance_flags = RESET_ALPHA LAZYADD(overlays_to_add, blood) overlay_state = "[base_state]-bones" diff --git a/code/modules/modular_computers/file_system/programs/generic/records.dm b/code/modules/modular_computers/file_system/programs/generic/records.dm index de1690d000f..030edf9bf7d 100644 --- a/code/modules/modular_computers/file_system/programs/generic/records.dm +++ b/code/modules/modular_computers/file_system/programs/generic/records.dm @@ -28,7 +28,7 @@ "physical_status" = list("Active", "*Deceased*", "*SSD*", "*Missing*", "Physically Unfit", "Disabled"), "criminal_status" = list("None", "*Arrest*", "Search", "Incarcerated", "Parolled", "Released"), "mental_status" = list("Stable", "*Insane*", "*Unstable*", "*Watch*"), - "medical" = list("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") + "medical" = list("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+", "SBS") ) /datum/computer_file/program/records/New() diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index 5f5c28a497a..b125b2bd1a9 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -175,28 +175,50 @@ vessel.add_reagent(/singleton/reagent/blood, amount, REAGENT_DATA(donor, /singleton/reagent/blood), temperature = species?.body_temperature) ..() -/proc/blood_incompatible(donor,receiver,donor_species,receiver_species) - if(!donor || !receiver) return 0 +/// Returns true if blood is incompatible, false otherwise. +/// Checks donor and receiver blood types, both args are strings. +/proc/blood_incompatible(var/donor, var/receiver, var/donor_species, var/receiver_species) + // Early exit so as to not check bad args + if(!donor || !receiver) + return FALSE + // Check species if(donor_species && receiver_species) if(donor_species != receiver_species) - return 1 + return TRUE + // Check for Synthetic Blood Substitute (SBS). + // SBS is supposed to be incompatible with non-SBS, and the other way around. + if(donor=="SBS" || receiver=="SBS") + if(donor!=receiver) + return TRUE + + // Parse the blood type var/donor_antigen = copytext(donor,1,length(donor)) var/receiver_antigen = copytext(receiver,1,length(receiver)) var/donor_rh = (findtext(donor,"+")>0) var/receiver_rh = (findtext(receiver,"+")>0) - if(donor_rh && !receiver_rh) return 1 + // Do the actual check + if(donor_rh && !receiver_rh) + return TRUE switch(receiver_antigen) if("A") - if(donor_antigen != "A" && donor_antigen != "O") return 1 + if(donor_antigen != "A" && donor_antigen != "O") return TRUE if("B") - if(donor_antigen != "B" && donor_antigen != "O") return 1 + if(donor_antigen != "B" && donor_antigen != "O") return TRUE if("O") - if(donor_antigen != "O") return 1 + if(donor_antigen != "O") return TRUE //AB is a universal receiver. - return 0 + return FALSE + +/mob/living/carbon/proc/get_blood_color() + if(dna?.b_type == "SBS") // synthetic blood substitute + return COLOR_SYNTH_BLOOD + else if(species) + return species.blood_color + else + return COLOR_HUMAN_BLOOD /mob/living/carbon/proc/get_blood_data() var/data = list() @@ -209,7 +231,7 @@ data["blood_type"] = "O+" if(species) data["species"] = species.bodytype - data["blood_colour"] = species.blood_color + data["blood_colour"] = get_blood_color() var/list/temp_chem = list() for(var/R in reagents.reagent_volumes) temp_chem[R] = REAGENT_VOLUME(reagents, R) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index cfa510d4eaa..6eafa569b61 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -1075,8 +1075,8 @@ Note that amputating the affected organ does in fact remove the infection from t var/obj/effect/decal/cleanable/blood/gibs/gore = new victim.species.single_gib_type(get_turf(victim)) if(victim.species.flesh_color) gore.fleshcolor = victim.species.flesh_color - if(victim.species.blood_color) - gore.basecolor = victim.species.blood_color + if(victim.get_blood_color()) + gore.basecolor = victim.get_blood_color() gore.update_icon() INVOKE_ASYNC(gore, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 0a6678b28e4..9e7f4623a9e 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -120,8 +120,8 @@ if(damage_type == DAMAGE_BRUTE && damage > 5) //weak hits shouldn't make you gush blood var/splatter_color = COLOR_HUMAN_BLOOD var/mob/living/carbon/human/H = target - if (istype(H) && H.species && H.species.blood_color) - splatter_color = H.species.blood_color + if (istype(H) && H.species && H.get_blood_color()) + splatter_color = H.get_blood_color() var/splatter_dir = starting ? get_dir(starting, target.loc) : dir new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.loc, splatter_dir, splatter_color) if(hit_effect) diff --git a/code/modules/reagents/Chemistry-Colours.dm b/code/modules/reagents/Chemistry-Colours.dm index 7d0c458cea7..ddda1ea66f2 100644 --- a/code/modules/reagents/Chemistry-Colours.dm +++ b/code/modules/reagents/Chemistry-Colours.dm @@ -30,4 +30,7 @@ if(!holder || isemptylist(REAGENT_DATA(holder, type))) return color + if(LAZYACCESS(holder.reagent_data[type], "blood_type") == "SBS") // synthetic blood substitute + return COLOR_SYNTH_BLOOD + return LAZYACCESS(holder.reagent_data[type], "blood_colour") || color // return default colour if not set diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm index 3e5b9908e49..26e818af2be 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm @@ -28,6 +28,7 @@ /singleton/reagent/blood/mix_data(var/list/newdata, var/newamount, var/datum/reagents/holder) var/list/data = ..() + if(LAZYACCESS(newdata, "trace_chem")) var/list/other_chems = LAZYACCESS(newdata, "trace_chem") if(!data) @@ -38,16 +39,26 @@ var/list/my_chems = data["trace_chem"] for(var/chem in other_chems) my_chems[chem] = my_chems[chem] + other_chems[chem] + var/datum/weakref/W = LAZYACCESS(data, "donor") var/mob/living/carbon/self = W?.resolve() - if(!(MODE_VAMPIRE in self?.mind?.antag_datums) && blood_incompatible(LAZYACCESS(newdata, "blood_type"), LAZYACCESS(data, "blood_type"), LAZYACCESS(newdata, "species"), LAZYACCESS(data, "species"))) - remove_self(newamount * 0.5, holder) // So the blood isn't *entirely* useless + + var/is_vampire = (MODE_VAMPIRE in self?.mind?.antag_datums) + var/incompatible = blood_incompatible(LAZYACCESS(newdata, "blood_type"), LAZYACCESS(data, "blood_type"), LAZYACCESS(newdata, "species"), LAZYACCESS(data, "species")) + + if(incompatible && !is_vampire) + // remove only half of the blood, so the blood isn't entirely useless + remove_self(newamount * 0.5, holder) + + // add coagulated blood, a toxin, that may cause kidney failure + // this is also more or less how it works in real life var/mob/living/carbon/human/recipient = holder.my_atom if(istype(recipient) && holder == recipient.vessel) recipient.reagents.add_reagent(/singleton/reagent/toxin/coagulated_blood, newamount * 0.5) // it has no effect if added to the vessel else holder.add_reagent(/singleton/reagent/toxin/coagulated_blood, newamount * 0.5) + . = data /singleton/reagent/blood/touch_turf(var/turf/simulated/T, var/amount, var/datum/reagents/holder) diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index f3029ff5549..af0f4b74165 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -166,12 +166,16 @@ if (REAGENT_VOLUME(reagents, /singleton/reagent/blood) && name != "empty IV bag") //Stops people mucking with bloodpacks that are filled to_chat(user, SPAN_NOTICE("You can't relabel [name] until it is empty!")) return - var/blood_name = tgui_input_list(user, "What would you like to label the IV bag?", "Label Selection", list("A+ blood", "A- blood", "B+ blood", "B- blood", "O+ blood", "O- blood", "AB+ blood", "AB- blood", "Saline Plus", "Cryonics mixture", "Other mixture", "Clear", "Cancel")) + + var/choices = list("A+ blood", "A- blood", "B+ blood", "B- blood", "O+ blood", "O- blood", "AB+ blood", "AB- blood", "SBS blood", "Saline Plus", "Cryonics mixture", "Other mixture", "Clear", "Cancel") + var/blood_name = tgui_input_list(user, "What would you like to label the IV bag?", "Label Selection", choices) if(blood_name == "Cancel") return + var/obj/item/i = user.get_active_hand() if(!i.ispen() || !in_range(user, src)) //Checks to see if pen is still held or bloodpack is in range return + if(blood_name == "Clear") blood_type = null name = initial(name) @@ -179,6 +183,7 @@ to_chat(user, SPAN_NOTICE("You clear the IV bag label.")) update_icon() return + blood_type = blood_name name = "\improper IV bag - [blood_type]" desc = "Contains fluids used for transfusions." @@ -268,6 +273,9 @@ /obj/item/reagent_containers/blood/OMinus blood_type = "O-" +/obj/item/reagent_containers/blood/sbs + blood_type = "SBS" + /obj/item/reagent_containers/blood/empty name = "empty IV bag" desc = "Seems pretty useless... Maybe if there were a way to fill it?" diff --git a/html/changelogs/DreamySkrell-white-blood.yml b/html/changelogs/DreamySkrell-white-blood.yml new file mode 100644 index 00000000000..e10b4840940 --- /dev/null +++ b/html/changelogs/DreamySkrell-white-blood.yml @@ -0,0 +1,7 @@ + +author: DreamySkrell + +delete-after: True + +changes: + - rscadd: "Adds Synthetic Blood Substitute." diff --git a/icons/clothing/accessories/blood_patch.dmi b/icons/clothing/accessories/blood_patch.dmi index 97ab4159524..a9959665922 100644 Binary files a/icons/clothing/accessories/blood_patch.dmi and b/icons/clothing/accessories/blood_patch.dmi differ diff --git a/maps/sccv_horizon/sccv_horizon-2_deck_2.dmm b/maps/sccv_horizon/sccv_horizon-2_deck_2.dmm index 002bbccefa7..c50bfe6363b 100644 --- a/maps/sccv_horizon/sccv_horizon-2_deck_2.dmm +++ b/maps/sccv_horizon/sccv_horizon-2_deck_2.dmm @@ -31611,6 +31611,8 @@ /obj/item/reagent_containers/blood/BPlus, /obj/item/reagent_containers/blood/BMinus, /obj/item/reagent_containers/blood/BMinus, +/obj/item/reagent_containers/blood/sbs, +/obj/item/reagent_containers/blood/sbs, /obj/item/reagent_containers/blood/APlus, /obj/item/reagent_containers/blood/APlus, /obj/item/reagent_containers/blood/AMinus, @@ -35451,6 +35453,7 @@ /area/horizon/grauwolf) "quV" = ( /obj/item/reagent_containers/blood/OMinus, +/obj/item/reagent_containers/blood/sbs, /obj/item/reagent_containers/blood/OMinus, /obj/item/reagent_containers/blood/OMinus, /obj/structure/closet/walllocker/medical/secure{ @@ -42757,6 +42760,7 @@ /area/rnd/xenobiology/foyer) "tSC" = ( /obj/item/reagent_containers/blood/OMinus, +/obj/item/reagent_containers/blood/sbs, /obj/item/reagent_containers/blood/OMinus, /obj/item/reagent_containers/blood/OMinus, /obj/structure/closet/walllocker/medical/secure{