diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index c18a3cecc0a..127d9f1712b 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -161,6 +161,8 @@
#define NOBLOODOVERLAY 20
///No augments, for monkeys in specific because they will turn into fucking freakazoids https://cdn.discordapp.com/attachments/326831214667235328/791313258912153640/102707682-fa7cad80-4294-11eb-8f13-8c689468aeb0.png
#define NOAUGMENTS 21
+///will be assigned a universal vampire themed last name shared by their department. this is preferenced!
+#define BLOOD_CLANS 22
//organ slots
#define ORGAN_SLOT_ADAMANTINE_RESONATOR "adamantine_resonator"
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index 5fd4b59b1c3..b36312439fd 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -32,5 +32,6 @@ GLOBAL_LIST_INIT(adjectives, world.file2list("strings/names/adjectives.txt"))
GLOBAL_LIST_INIT(gross_adjectives, world.file2list("strings/names/gross_adjectives.txt"))
GLOBAL_LIST_INIT(hive_names, world.file2list("strings/names/hive_names.txt"))
GLOBAL_LIST_INIT(dream_strings, world.file2list("strings/dreamstrings.txt"))
+GLOBAL_LIST_INIT(vampire_house_names, world.file2list("strings/names/vampire_house_names.txt"))
//loaded on startup because of "
//would include in rsc if ' was used
diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm
index a68a74916eb..3a5537d97b0 100644
--- a/code/modules/client/preferences/_preference.dm
+++ b/code/modules/client/preferences/_preference.dm
@@ -15,8 +15,11 @@
/// The priority at which names are decided, needed for proper randomization.
#define PREFERENCE_PRIORITY_NAMES 5
+/// Preferences that aren't names, but change the name changes set by PREFERENCE_PRIORITY_NAMES.
+#define PREFERENCE_PRIORITY_NAME_MODIFICATIONS 6
+
/// The maximum preference priority, keep this updated, but don't use it for `priority`.
-#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_NAMES
+#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_NAME_MODIFICATIONS
/// For choiced preferences, this key will be used to set display names in constant data.
#define CHOICED_PREFERENCE_DISPLAY_NAMES "display_names"
diff --git a/code/modules/client/preferences/species_features/vampire.dm b/code/modules/client/preferences/species_features/vampire.dm
new file mode 100644
index 00000000000..462b7ce099d
--- /dev/null
+++ b/code/modules/client/preferences/species_features/vampire.dm
@@ -0,0 +1,48 @@
+/datum/preference/choiced/vampire_status
+ savefile_key = "feature_vampire_status"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_FEATURES
+ priority = PREFERENCE_PRIORITY_NAME_MODIFICATIONS //this will be overwritten by names otherwise
+ main_feature_name = "Vampire status"
+ should_generate_icons = TRUE
+ relevant_species_trait = BLOOD_CLANS
+
+/datum/preference/choiced/vampire_status/create_default_value()
+ return "Inoculated" //eh, have em try out the mechanic first
+
+/datum/preference/choiced/vampire_status/init_possible_values()
+ var/list/values = list()
+
+ values["Inoculated"] = icon('icons/obj/drinks.dmi', "bloodglass")
+ values["Outcast"] = icon('icons/obj/bloodpack.dmi', "generic_bloodpack")
+
+ return values
+
+///list that stores a vampire house name for each department
+GLOBAL_LIST_EMPTY(vampire_houses)
+
+/datum/preference/choiced/vampire_status/apply_to_human(mob/living/carbon/human/target, value)
+ if(value != "Inoculated")
+ return
+
+ //find and setup the house (department) this vampire is joining
+ var/datum/job_department/vampire_house
+ var/datum/job/vampire_job = SSjob.GetJob(target.job)
+ if(!vampire_job) //no job or no mind LOSERS
+ return
+ var/list/valid_departments = (SSjob.joinable_departments.Copy()) - list(/datum/job_department/silicon, /datum/job_department/undefined)
+ for(var/datum/job_department/potential_house as anything in valid_departments)
+ if(vampire_job in potential_house.department_jobs)
+ vampire_house = potential_house
+ break
+ if(!vampire_house) //sillycones
+ return
+ if(!GLOB.vampire_houses[vampire_house.department_name])
+ GLOB.vampire_houses[vampire_house.department_name] = pick(GLOB.vampire_house_names)
+ var/house_name = GLOB.vampire_houses[vampire_house.department_name]
+
+ //modify name (Platos Syrup > Platos de Lioncourt)
+ var/first_space_index = findtextEx(target.real_name, " ")
+ var/new_name = copytext(target.real_name, 1, first_space_index + 1)
+ new_name += house_name
+ target.fully_replace_character_name(target.real_name, new_name)
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 4676f78c103..d633f517e2d 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -409,6 +409,8 @@
var/require_human = CONFIG_GET(flag/enforce_human_authority) && (job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ src.job = job.title
+
if(fully_randomize)
if(require_human)
player_client.prefs.randomise_appearance_prefs(~RANDOMIZE_SPECIES)
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 3a9586e3fda..2baa07d5dde 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -1,8 +1,23 @@
+
+///how many vampires exist in each house
+#define VAMPIRES_PER_HOUSE 5
+///maximum a vampire will drain, they will drain less if they hit their cap
+#define VAMP_DRAIN_AMOUNT 50
+
/datum/species/vampire
name = "Vampire"
id = SPECIES_VAMPIRE
default_color = "FFFFFF"
- species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD, HAS_FLESH, HAS_BONE)
+ species_traits = list(
+ EYECOLOR,
+ HAIR,
+ FACEHAIR,
+ LIPS,
+ DRINKSBLOOD,
+ HAS_FLESH,
+ HAS_BONE,
+ BLOOD_CLANS,
+ )
inherent_traits = list(
TRAIT_ADVANCEDTOOLUSER,
TRAIT_CAN_STRIP,
@@ -18,51 +33,42 @@
mutanttongue = /obj/item/organ/tongue/vampire
limbs_id = SPECIES_HUMAN
skinned_type = /obj/item/stack/sheet/animalhide/human
+ ///some starter text sent to the vampire initially, because vampires have shit to do to stay alive
var/info_text = "You are a Vampire. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability."
- var/obj/effect/proc_holder/spell/targeted/shapeshift/bat/batform //attached to the datum itself to avoid cloning memes, and other duplicates
/datum/species/vampire/check_roundstart_eligible()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
return TRUE
return ..()
-/datum/species/vampire/on_species_gain(mob/living/carbon/human/C, datum/species/old_species)
+/datum/species/vampire/on_species_gain(mob/living/carbon/human/new_vampire, datum/species/old_species)
. = ..()
- to_chat(C, "[info_text]")
- C.skin_tone = "albino"
- C.update_body(0)
- if(isnull(batform))
- batform = new
- C.AddSpell(batform)
- C.set_safe_hunger_level()
+ to_chat(new_vampire, "[info_text]")
+ new_vampire.skin_tone = "albino"
+ new_vampire.update_body(0)
+ new_vampire.set_safe_hunger_level()
-/datum/species/vampire/on_species_loss(mob/living/carbon/C)
+/datum/species/vampire/spec_life(mob/living/carbon/human/vampire, delta_time, times_fired)
. = ..()
- if(!isnull(batform))
- C.RemoveSpell(batform)
- QDEL_NULL(batform)
-
-/datum/species/vampire/spec_life(mob/living/carbon/human/C, delta_time, times_fired)
- . = ..()
- if(istype(C.loc, /obj/structure/closet/crate/coffin))
- C.heal_overall_damage(2 * delta_time, 2 * delta_time, 0, BODYPART_ORGANIC)
- C.adjustToxLoss(-2 * delta_time)
- C.adjustOxyLoss(-2 * delta_time)
- C.adjustCloneLoss(-2 * delta_time)
+ if(istype(vampire.loc, /obj/structure/closet/crate/coffin))
+ vampire.heal_overall_damage(2 * delta_time, 2 * delta_time, 0, BODYPART_ORGANIC)
+ vampire.adjustToxLoss(-2 * delta_time)
+ vampire.adjustOxyLoss(-2 * delta_time)
+ vampire.adjustCloneLoss(-2 * delta_time)
return
- C.blood_volume -= 0.125 * delta_time
- if(C.blood_volume <= BLOOD_VOLUME_SURVIVE)
- to_chat(C, span_danger("You ran out of blood!"))
- var/obj/shapeshift_holder/H = locate() in C
- if(H)
- H.shape.dust() //make sure we're killing the bat if you are out of blood, if you don't it creates weird situations where the bat is alive but the caster is dusted.
- C.dust()
- var/area/A = get_area(C)
+ vampire.blood_volume -= 0.125 * delta_time
+ if(vampire.blood_volume <= BLOOD_VOLUME_SURVIVE)
+ to_chat(vampire, span_danger("You ran out of blood!"))
+ var/obj/shapeshift_holder/holder = locate() in vampire
+ if(holder)
+ holder.shape.dust() //vampires do not have batform anymore, but this would still lead to very weird stuff with other shapeshift holders
+ vampire.dust()
+ var/area/A = get_area(vampire)
if(istype(A, /area/service/chapel))
- to_chat(C, span_warning("You don't belong here!"))
- C.adjustFireLoss(10 * delta_time)
- C.adjust_fire_stacks(3 * delta_time)
- C.IgniteMob()
+ to_chat(vampire, span_warning("You don't belong here!"))
+ vampire.adjustFireLoss(10 * delta_time)
+ vampire.adjust_fire_stacks(3 * delta_time)
+ vampire.IgniteMob()
/datum/species/vampire/check_species_weakness(obj/item/weapon, mob/living/attacker)
if(istype(weapon, /obj/item/nullrod/whip))
@@ -75,8 +81,6 @@
color = "#1C1C1C"
COOLDOWN_DECLARE(drain_cooldown)
-#define VAMP_DRAIN_AMOUNT 50
-
/datum/action/item_action/organ_action/vampire
name = "Drain Victim"
desc = "Leech blood from any carbon victim you are passively grabbing."
@@ -121,18 +125,9 @@
if(!victim.blood_volume)
to_chat(H, span_notice("You finish off [victim]'s blood supply."))
-#undef VAMP_DRAIN_AMOUNT
-
-
/obj/item/organ/heart/vampire
name = "vampire heart"
color = "#1C1C1C"
-
-/obj/effect/proc_holder/spell/targeted/shapeshift/bat
- name = "Bat Form"
- desc = "Take on the shape a space bat."
- invocation = "Squeak!"
- charge_max = 50
- cooldown_min = 50
- shapeshift_type = /mob/living/simple_animal/hostile/retaliate/bat
+#undef VAMPIRES_PER_HOUSE
+#undef VAMP_DRAIN_AMOUNT
diff --git a/icons/obj/bloodpack.dmi b/icons/obj/bloodpack.dmi
index efcf670da59..fdbc5bceb9e 100644
Binary files a/icons/obj/bloodpack.dmi and b/icons/obj/bloodpack.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index 4fe4048d43c..d0dade81bab 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/strings/names/vampire_house_names.txt b/strings/names/vampire_house_names.txt
new file mode 100644
index 00000000000..53cf631c6f6
--- /dev/null
+++ b/strings/names/vampire_house_names.txt
@@ -0,0 +1,60 @@
+Aundae
+Banu Haqim
+Berne
+Bounts
+Brando
+Brujah
+Brunestud
+Cross
+Cruz
+Culebra
+Cullen
+Czimisce
+de Jund
+de Lioncourt
+de Ministry
+de Spingbabble
+der Catwalk
+Derosso
+Dimetrescu
+Dimir
+Dracula
+Ego
+Gangrel
+Gilbert
+Guine
+Hecata
+LaCroix
+Lasombra
+Malack
+Malkavian
+Markov
+Maryam
+Maximov
+Munster
+Myotis
+Nosferatu
+Quarra
+Ravencroft
+Ravnos
+Salubri
+San'layn
+Sanguine
+Scarlet
+Selene
+Sepperin
+Strauss
+Toga
+Toreador
+Tremere
+Valentine
+Valvatorez
+Ventrue
+Victoria
+Voermann
+von Block
+von Count
+von Ghoulist
+von Grout
+von Sport
+Yakshi
diff --git a/tgstation.dme b/tgstation.dme
index 79c4a6625b0..21e7b1de2a0 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2255,6 +2255,7 @@
#include "code\modules\client\preferences\species_features\lizard.dm"
#include "code\modules\client\preferences\species_features\moth.dm"
#include "code\modules\client\preferences\species_features\mutants.dm"
+#include "code\modules\client\preferences\species_features\vampire.dm"
#include "code\modules\client\verbs\ooc.dm"
#include "code\modules\client\verbs\ping.dm"
#include "code\modules\client\verbs\reset_held_keys.dm"
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
index dab61934472..c4b9d80764a 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
@@ -60,3 +60,8 @@ export const underwear_color: Feature = {
name: "Underwear color",
component: FeatureColorInput,
};
+
+export const feature_vampire_status: Feature = {
+ name: "Vampire status",
+ component: FeatureDropdownInput,
+};