diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index b1b820eaf30e..3c4855c0fcb7 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -264,6 +264,8 @@
#define TRAIT_SHIFTY_EYES "shifty_eyes"
#define TRAIT_ANXIOUS "anxious"
#define TRAIT_SEE_REAGENTS "see_reagents"
+#define TRAIT_DRINKSBLOOD "drinks_blood"
+
// common trait sources
#define TRAIT_GENERIC "generic"
diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm
index 1625386b7267..f4ba6a4984df 100644
--- a/code/datums/diseases/advance/advance.dm
+++ b/code/datums/diseases/advance/advance.dm
@@ -479,3 +479,96 @@
/datum/disease/advance/proc/totalTransmittable()
return properties["transmittable"]
+
+/datum/disease/advance/proc/random_disease_name(var/atom/diseasesource)//generates a name for a disease depending on its symptoms and where it comes from
+ var/list/prefixes = list("Spacer's ", "Space ", "Infectious ","Viral ", "The ", "[pick(GLOB.first_names)]'s ", "[pick(GLOB.last_names)]'s ", "Acute ")//prefixes that arent tacked to the body need spaces after the word
+ var/list/bodies = list(pick("[pick(GLOB.first_names)]", "[pick(GLOB.last_names)]"), "Space", "Disease", "Noun", "Cold", "Germ", "Virus")
+ var/list/suffixes = list("ism", "itis", "osis", "itosis", " #[rand(1,10000)]", "-[rand(1,100)]", "s", "y", "ovirus", " Bug", " Infection", " Disease", " Complex", " Syndrome", " Sickness") //suffixes that arent tacked directly on need spaces before the word
+ if(stealth >=2)
+ prefixes += "Crypto "
+ switch(max(resistance - (symptoms.len / 2), 1))
+ if(1)
+ suffixes += "-alpha"
+ if(2)
+ suffixes += "-beta"
+ if(3)
+ suffixes += "-gamma"
+ if(4)
+ suffixes += "-delta"
+ if(5)
+ suffixes += "-epsilon"
+ if(6)
+ suffixes += pick("-zeta", "-eta", "-theta", "-iota")
+ if(7)
+ suffixes += pick("-kappa", "-lambda")
+ if(8)
+ suffixes += pick("-mu", "-nu", "-xi", "-omicron")
+ if(9)
+ suffixes += pick("-pi", "-rho", "-sigma", "-tau")
+ if(10)
+ suffixes += pick("-upsilon", "-phi", "-chi", "-psi")
+ if(11 to INFINITY)
+ suffixes += "-omega"
+ prefixes += "Robust "
+ switch(transmission - symptoms.len)
+ if(-INFINITY to 2)
+ prefixes += "Bloodborne "
+ if(3)
+ prefixes += list("Mucous ", "Kissing ")
+ if(4)
+ prefixes += "Contact "
+ suffixes += " Flu"
+ if(5 to INFINITY)
+ prefixes += "Airborne "
+ suffixes += " Plague"
+ switch(severity)
+ if(-INFINITY to 0)
+ prefixes += "Altruistic "
+ if(1 to 2)
+ prefixes += "Benign "
+ if(3 to 4)
+ prefixes += "Malignant "
+ if(5)
+ prefixes += "Terminal "
+ bodies += "Death"
+ if(6 to INFINITY)
+ prefixes += "Deadly "
+ bodies += "Death"
+ if(diseasesource)
+ if(ishuman(diseasesource))
+ var/mob/living/carbon/human/H = diseasesource
+ prefixes += pick("[H.first_name()]'s", "[H.name]'s", "[H.job]'s", "[H.dna.species]'s")
+ bodies += pick("[H.first_name()]", "[H.job]", "[H.dna.species]")
+ if(islizard(H) || iscatperson(H))//add rat-origin prefixes to races that eat rats
+ prefixes += list("Vermin ", "Zoo", "Maintenance ")
+ bodies += list("Rat", "Maint")
+ else switch(diseasesource.type)
+ if(/mob/living/simple_animal/pet/hamster/vector)
+ prefixes += list("Vector's ", "Hamster ")
+ bodies += list("Freebie")
+ if(/obj/effect/decal/cleanable)
+ prefixes += list("Bloody ", "Maintenance ")
+ bodies += list("Maint")
+ if(/mob/living/simple_animal/mouse)
+ prefixes += list("Vermin ", "Zoo", "Maintenance ")
+ bodies += list("Rat", "Maint")
+ if(/obj/item/reagent_containers/syringe)
+ prefixes += list("Junkie ", "Maintenance ")
+ bodies += list("Needle", "Maint")
+ if(/obj/item/fugu_gland)
+ prefixes += "Wumbo"
+ if(/obj/item/organ/lungs)
+ prefixes += "Miasmic "
+ bodies += list("Stench", "Lung")
+ for(var/datum/symptom/Symptom as() in symptoms)
+ if(!Symptom.neutered)
+ prefixes += Symptom.prefixes
+ bodies += Symptom.bodies
+ suffixes += Symptom.suffixes
+ switch(rand(1, 3))
+ if(1)
+ return "[pick(prefixes)][pick(bodies)]"
+ if(2)
+ return "[pick(prefixes)][pick(bodies)][pick(suffixes)]"
+ if(3)
+ return "[pick(bodies)][pick(suffixes)]"
diff --git a/code/datums/diseases/advance/presets.dm b/code/datums/diseases/advance/presets.dm
index cf261da1df10..1061c02224d8 100644
--- a/code/datums/diseases/advance/presets.dm
+++ b/code/datums/diseases/advance/presets.dm
@@ -32,6 +32,14 @@
symptoms = list(new/datum/symptom/tumor,new/datum/symptom/sneeze,new/datum/symptom/fever,new/datum/symptom/shivering,new/datum/symptom/itching,new/datum/symptom/cough)
..()
+/datum/disease/advance/vampirism
+ copy_type = /datum/disease/advance
+
+/datum/disease/advance/necropolis/New()
+ name = "Hematophagy"
+ symptoms = list(/datum/symptom/vampirism)
+ ..()
+
//Randomly generated Disease, for virus crates and events
/datum/disease/advance/random
diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm
index 265ede7aa281..d34307e19c68 100644
--- a/code/datums/diseases/advance/symptoms/heal.dm
+++ b/code/datums/diseases/advance/symptoms/heal.dm
@@ -492,3 +492,235 @@
if(L.heal_damage(heal_amt/parts.len, heal_amt/parts.len, null, BODYPART_ORGANIC))
M.update_damage_overlays()
return 1
+
+
+/datum/symptom/vampirism
+ name = "Hematophagy"
+ desc = "The host absorbs blood from external sources, and seemlessly reintegrates it into their own bloodstream, regardless of its bloodtype or how it was ingested. However, the virus also slowly consumes the host's blood"
+ stealth = 1
+ resistance = -2
+ stage_speed = 1
+ transmittable = 2
+ level = 9
+ symptom_delay_min = 1
+ symptom_delay_max = 1
+ bodies = list("Blood")
+ var/bloodpoints = 0
+ var/maxbloodpoints = 50
+ var/bloodtypearchive
+ var/bruteheal = FALSE
+ var/aggression = FALSE
+ var/vampire = FALSE
+ var/mob/living/carbon/human/bloodbag
+ threshold_descs = list(
+ "Transmission 4" = "Host appears to die when falling into a coma.",
+ "Transmission 6" = "The virus aggressively assimilates blood, resulting in contiguous blood pools being absorbed by the virus, as well as sucking blood out of open wounds of subjects in physical contact with the host.",
+ "Stage Speed 7" = "The virus grows more aggressive, assimilating blood and healing at a faster rate, but also draining the host's blood quicker",
+ )
+
+
+
+
+/datum/symptom/vampirism/Start(datum/disease/advance/A)
+ if(!..())
+ return
+ if(A.totalTransmittable() >= 4)
+ bruteheal = TRUE
+ if(A.totalTransmittable() >= 6)
+ aggression = TRUE
+ maxbloodpoints += 50
+ if(A.totalStageSpeed() >= 7)
+ power += 1
+ if((A.totalStealth() >= 2) && (A.totalTransmittable() >= 6) && A.process_dead) //this is low transmission for 2 reasons: transmission is hard to raise, especially with stealth, and i dont want this to be obligated to be transmittable
+ vampire = TRUE
+ maxbloodpoints += 50
+ power += 1
+ if(ishuman(A.affected_mob) && A.affected_mob.get_blood_id() == /datum/reagent/blood)
+ var/mob/living/carbon/human/H = A.affected_mob
+ bloodtypearchive = H.dna.blood_type
+ H.dna.blood_type = "U"
+
+/datum/symptom/vampirism/Activate(datum/disease/advance/A)
+ if(!..())
+ return
+ var/mob/living/carbon/M = A.affected_mob
+ switch(A.stage)
+ if(1 to 4)
+ if(prob(5))
+ to_chat(M, "[pick("You feel a craving for iron?", "You feel very thirsty and theres a metallic taste in your mouth", "The thought of biting a neck crosses your mind...")]")
+ if(5)
+ ADD_TRAIT(A.affected_mob, TRAIT_DRINKSBLOOD, DISEASE_TRAIT)
+ var/grabbedblood = succ(M) //before adding sucked blood to bloodpoints, immediately try to heal bloodloss
+ if(M.blood_volume < BLOOD_VOLUME_NORMAL && M.get_blood_id() == /datum/reagent/blood)
+ var/missing = BLOOD_VOLUME_NORMAL - M.blood_volume
+ var/inflated = grabbedblood * 4
+ M.blood_volume = min(M.blood_volume + inflated, BLOOD_VOLUME_NORMAL)
+ bloodpoints += round(max(0, (inflated - missing)/4))
+ else if((M.blood_volume >= BLOOD_VOLUME_NORMAL + 4) && (bloodpoints < maxbloodpoints))//so drinking blood accumulates bloodpoints
+ M.blood_volume = (M.blood_volume - 4)
+ bloodpoints += 1
+ else
+ bloodpoints += max(0, grabbedblood)
+ for(var/I in 1 to power)//power doesnt increase efficiency, just usage.
+ if(bloodpoints > 0)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.bleed_rate >= 2 && bruteheal && bloodpoints)
+ bloodpoints -= 1
+ H.bleed_rate = max(0, (H.bleed_rate - 2))
+ if(M.blood_volume < BLOOD_VOLUME_NORMAL && M.get_blood_id() == /datum/reagent/blood) //bloodloss is prioritized over healing brute
+ bloodpoints -= 1
+ M.blood_volume = max((M.blood_volume + 3 * power), BLOOD_VOLUME_NORMAL) //bloodpoints are valued at 4 units of blood volume per point, so this is diminished
+ else if(bruteheal && M.getBruteLoss())
+ bloodpoints -= 1
+ M.heal_overall_damage(2, required_status = BODYTYPE_ORGANIC)
+ if(prob(60) && !M.stat)
+ bloodpoints -- //you cant just accumulate blood and keep it as a battery of healing. the quicker the symptom is, the faster your bloodpoints decay
+ else if(prob(20) && M.blood_volume >= BLOOD_VOLUME_BAD)//the virus continues to extract blood if you dont have any stored up. higher probability due to BP value
+ M.blood_volume = (M.blood_volume - 1)
+
+ if(!bloodpoints && prob(3))
+ to_chat(M, "[pick("You need blood!".", "Your throat is dry...", "Your heart flutters alarmingly...")]")
+
+/datum/symptom/vampirism/End(datum/disease/advance/A)
+ . = ..()
+ REMOVE_TRAIT(A.affected_mob, TRAIT_DRINKSBLOOD, DISEASE_TRAIT)
+ if(bloodtypearchive && ishuman(A.affected_mob))
+ var/mob/living/carbon/human/H = A.affected_mob
+ H.dna.blood_type = bloodtypearchive
+
+/datum/symptom/vampirism/proc/succ(mob/living/carbon/M) //you dont need the blood reagent to suck blood. however, you need to have blood, or at least a shared blood reagent, for most of the other uses
+ var/gainedpoints = 0
+ if(bloodbag && !bloodbag.blood_volume) //we've exsanguinated them!
+ bloodbag = null
+ if(ishuman(M) && M.stat == DEAD && vampire)
+ var/mob/living/carbon/human/H = M
+ var/possibledist = power + 1
+ if(M.get_blood_id() != /datum/reagent/blood)
+ possibledist = 1
+ if(!(NOBLOOD in H.dna.species.species_traits)) //if you dont have blood, well... sucks to be you
+ H.setOxyLoss(0,0) //this is so a crit person still revives if suffocated
+ if(bloodpoints >= 200 && H.health > 0 && H.blood_volume >= BLOOD_VOLUME_NORMAL) //note that you need to actually need to heal, so a maxed out virus won't be bringing you back instantly in most cases. *even so*, if this needs to be nerfed ill do it in a heartbeat
+ H.revive(0)
+ H.visible_message("[H.name]'s skin takes on a rosy hue as they begin moving. They live again!", "As your body fills with fresh blood, you feel your limbs once more, accompanied by an insatiable thirst for blood.")
+ bloodpoints = 0
+ return 0
+ else if(bloodbag && bloodbag.blood_volume && (bloodbag.stat || bloodbag.bleed_rate))
+ if(get_dist(bloodbag, H) <= 1 && bloodbag.z == H.z)
+ var/amt = ((bloodbag.stat * 2) + 2) * power
+ var/excess = max(((min(amt, bloodbag.blood_volume) - (BLOOD_VOLUME_NORMAL - H.blood_volume)) / 2), 0)
+ H.blood_volume = min(H.blood_volume + min(amt, bloodbag.blood_volume), BLOOD_VOLUME_NORMAL)
+ bloodbag.blood_volume = max(bloodbag.blood_volume - amt, 0)
+ bloodpoints += max(excess, 0)
+ playsound(bloodbag.loc, 'sound/magic/exit_blood.ogg', 10, 1)
+ bloodbag.visible_message("Blood flows from [bloodbag.name]'s wounds into [H.name]'s corpse!", "Blood flows from your wounds into [H.name]'s corpse!")
+ else if(get_dist(bloodbag, H) >= possibledist) //they've been taken out of range.
+ bloodbag = null
+ return
+ else if(bloodpoints >= 2)
+ var/turf/T = H.loc
+ var/obj/effect/decal/cleanable/blood/influenceone = (locate(/obj/effect/decal/cleanable/blood) in H.loc)
+ if(!influenceone && bloodpoints >= 2)
+ H.add_splatter_floor(T)
+ playsound(T, 'sound/effects/splat.ogg', 50, 1)
+ bloodpoints -= 2
+ return 0
+ else
+ var/todir = get_dir(H, bloodbag)
+ var/targetloc = bloodbag.loc
+ var/dist = get_dist(H, bloodbag)
+ for(var/i=0 to dist)
+ T = get_step(T, todir)
+ todir = get_dir(T, bloodbag)
+ var/obj/effect/decal/cleanable/blood/influence = (locate(/obj/effect/decal/cleanable/blood) in T)
+ if(!influence && bloodpoints >= 2)
+ H.add_splatter_floor(T)
+ playsound(T, 'sound/effects/splat.ogg', 50, 1)
+ bloodpoints -= 2
+ return 0
+ else if(T == targetloc && bloodpoints >= 2)
+ bloodbag.throw_at(H, 1, 1)
+ bloodpoints -= 2
+ bloodbag.visible_message("A current of blood pushes [bloodbag.name] towards [H.name]'s corpse!")
+ playsound(bloodbag.loc, 'sound/magic/exit_blood.ogg', 25, 1)
+ return 0
+ else
+ var/list/candidates = list()
+ for(var/mob/living/carbon/human/C in ohearers(min(bloodpoints/4, possibledist), H))
+ if(NOBLOOD in C.dna.species.species_traits)
+ continue
+ if(C.stat && C.blood_volume && C.get_blood_id() == H.get_blood_id())
+ candidates += C
+ for(var/prospect in candidates)
+ candidates[prospect] = 1
+ if(ishuman(prospect))
+ var/mob/living/carbon/human/candidate = prospect
+ candidates[prospect] += (candidate.stat - 1)
+ candidates[prospect] += (3 - get_dist(candidate, H)) * 2
+ candidates[prospect] += round(candidate.blood_volume / 150)
+ bloodbag = pickweight(candidates) //dont return here
+
+ if(bloodpoints >= maxbloodpoints)
+ return 0
+ if(ishuman(M) && aggression) //first, try to suck those the host is actively grabbing
+ var/mob/living/carbon/human/H = M
+ if(H.pulling && ishuman(H.pulling)) //grabbing is handled with the disease instead of the component, so the component doesn't have to be processed
+ var/mob/living/carbon/human/C = H.pulling
+ if(!C.bleed_rate && vampire && C.can_inject() && H.grab_state && C.get_blood_id() == H.get_blood_id() && !(NOBLOOD in C.dna.species.species_traits))//aggressive grab as a "vampire" starts the target bleeding
+ C.bleed_rate += 1
+ C.visible_message("Wounds open on [C.name]'s skin as [H.name] grips them tightly!", "You begin bleeding at [H.name]'s touch!")
+ if(C.blood_volume && C.can_inject() &&(C.bleed_rate && (!C.bleedsuppress || vampire )) && C.get_blood_id() == H.get_blood_id() && !(NOBLOOD in C.dna.species.species_traits))
+ var/amt = (H.grab_state + C.stat + 2) * power
+ if(C.blood_volume)
+ var/excess = max(((min(amt, C.blood_volume) - (BLOOD_VOLUME_NORMAL - H.blood_volume)) / 4), 0)
+ H.blood_volume = min(H.blood_volume + min(amt, C.blood_volume), BLOOD_VOLUME_NORMAL)
+ C.blood_volume = max(C.blood_volume - amt, 0)
+ gainedpoints = CLAMP(excess, 0, maxbloodpoints - bloodpoints)
+ C.visible_message("Blood flows from [C.name]'s wounds into [H.name]!", "Blood flows from your wounds into [H.name]!")
+ playsound(C.loc, 'sound/magic/exit_blood.ogg', 25, 1)
+ return gainedpoints
+ if(locate(/obj/effect/decal/cleanable/blood) in M.loc)
+ var/obj/effect/decal/cleanable/blood/initialstain = (locate(/obj/effect/decal/cleanable/blood) in M.loc)
+ var/list/stains = list()
+ var/suckamt = power + 1
+ if(aggression)
+ for(var/obj/effect/decal/cleanable/blood/contiguousstain in orange(1, M))
+ if(suckamt)
+ suckamt --
+ stains += contiguousstain
+ if(suckamt)
+ suckamt --
+ stains += initialstain
+ for(var/obj/effect/decal/cleanable/blood/stain in stains) //this doesnt use switch(type) because that doesnt check subtypes
+ if(istype(stain, /obj/effect/decal/cleanable/blood/gibs/old))
+ gainedpoints += 3
+ qdel(stain)
+ else if(istype(stain, /obj/effect/decal/cleanable/blood/old))
+ gainedpoints += 1
+ qdel(stain)
+ else if(istype(stain, /obj/effect/decal/cleanable/blood/gibs))
+ gainedpoints += 5
+ qdel(stain)
+ else if(istype(stain, /obj/effect/decal/cleanable/blood/footprints) || istype(stain, /obj/effect/decal/cleanable/blood/tracks) || istype(stain, /obj/effect/decal/cleanable/blood/drip))
+ qdel(stain)//these types of stain are generally very easy to make, we don't use these
+ else if(istype(stain, /obj/effect/decal/cleanable/blood))
+ gainedpoints += 2
+ qdel(stain)
+ if(gainedpoints)
+ playsound(M.loc, 'sound/magic/exit_blood.ogg', 50, 1)
+ M.visible_message("Blood flows from the floor into [M.name]!", "You consume the errant blood")
+ return CLAMP(gainedpoints, 0, maxbloodpoints - bloodpoints)
+ if(ishuman(M) && aggression)//finally, attack mobs touching the host.
+ var/mob/living/carbon/human/H = M
+ for(var/mob/living/carbon/human/C in ohearers(1, H))
+ if(NOBLOOD in C.dna.species.species_traits)
+ continue
+ if((C.pulling && C.pulling == H) || (C.loc == H.loc) && C.bleed_rate && C.get_blood_id() == H.get_blood_id())
+ var/amt = (2 * power)
+ if(C.blood_volume)
+ var/excess = max(((min(amt, C.blood_volume) - (BLOOD_VOLUME_NORMAL - H.blood_volume)) / 4 * power), 0)
+ H.blood_volume = min(H.blood_volume + min(amt, C.blood_volume), BLOOD_VOLUME_NORMAL)
+ C.blood_volume = max(C.blood_volume - amt, 0)
+ gainedpoints += CLAMP(excess, 0, maxbloodpoints - bloodpoints)
+ C.visible_message("Blood flows from [C.name]'s wounds into [H.name]!", "Blood flows from your wounds into [H.name]!")
+ return CLAMP(gainedpoints, 0, maxbloodpoints - bloodpoints)
diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm
index 54c2afeee2b0..236a288b6abb 100644
--- a/code/modules/reagents/reagent_containers/bottle.dm
+++ b/code/modules/reagents/reagent_containers/bottle.dm
@@ -337,6 +337,11 @@
icon_state = "mortar"
spawned_disease = /datum/disease/advance/necropolis
+/obj/item/reagent_containers/glass/bottle/vampirism
+ name = "Hematophagy culture bottle"
+ desc = "A small bottle. Contains a sample of Hematophagy."
+ spawned_disease = /datum/disease/advance/vampirism
+
//Oldstation.dmm chemical storage bottles
/obj/item/reagent_containers/glass/bottle/hydrogen
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 0b1ca2d1ae14..1d40597397c2 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -1940,6 +1940,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
restricted_roles = list("Assistant")
surplus = 0
+/datum/uplink_item/role_restricted/necroseed
+ name = "The Vampire Virus (should this be the name in the uplink)"
+ desc = "PLACEHOLDEr."
+ item = /obj/item/reagent_containers/glass/bottle/vampirism
+ cost = 6
+ restricted_roles = list("Virologist")
+ surplus = 0
+
/datum/uplink_item/role_restricted/oldtoolboxclean
name = "Ancient Toolbox"
desc = "An iconic toolbox design notorious with Assistants everywhere, this design was especially made to become more robust the more telecrystals it has inside it! Tools and insulated gloves included."