SINGLE USE: [name] can only be used once per night."
+
+/datum/action/cooldown/vampire/Destroy()
+ vampiredatum_power = null
+ return ..()
+
+/datum/action/cooldown/vampire/IsAvailable(feedback = FALSE)
+ return next_use_time <= world.time
+
+/datum/action/cooldown/vampire/Grant(mob/user)
+ . = ..()
+ var/datum/antagonist/vampire/vampiredatum = IS_VAMPIRE(owner)
+ if(vampiredatum)
+ vampiredatum_power = vampiredatum
+
+//This is when we CLICK on the ability Icon, not USING.
+/datum/action/cooldown/vampire/Trigger(trigger_flags, atom/target)
+ if(active)
+ DeactivatePower()
+ return FALSE
+ if(!can_pay_cost() || !can_use())
+ return FALSE
+ pay_cost()
+ ActivatePower(trigger_flags)
+ if(!(power_flags & BP_AM_TOGGLE) || !active)
+ StartCooldown()
+ return TRUE
+
+///Called when the Power is upgraded.
+/datum/action/cooldown/vampire/proc/upgrade_power()
+ level_current++
+ // Decrease cooldown time
+ if(power_flags & !BP_AM_STATIC_COOLDOWN)
+ cooldown_time = max(initial(cooldown_time) / 2, initial(cooldown_time) - (initial(cooldown_time) / 16 * (level_current - 1)))
+
+/datum/action/cooldown/vampire/proc/can_pay_cost()
+ if(!owner || !owner.mind)
+ return FALSE
+ // Cooldown?
+ if(!COOLDOWN_FINISHED(src, next_use_time))
+ owner.balloon_alert(owner, "power unavailable!")
+ return FALSE
+ if(!vampiredatum_power)
+ var/mob/living/living_owner = owner
+ if(!HAS_TRAIT(living_owner, TRAIT_NO_BLOOD) && living_owner.blood_volume < bloodcost)
+ to_chat(owner, "You need at least [bloodcost] blood to activate [name]")
+ return FALSE
+ return TRUE
+
+ // Have enough blood? Vampires in a Frenzy don't need to pay them
+ if(vampiredatum_power.frenzied)
+ return TRUE
+ if(vampiredatum_power.vampire_blood_volume < bloodcost)
+ to_chat(owner, "You need at least [bloodcost] blood to activate [name]")
+ return FALSE
+ return TRUE
+
+///Checks if the Power is available to use.
+/datum/action/cooldown/vampire/proc/can_use()
+ var/mob/living/carbon/user = owner
+
+ if(!owner)
+ return FALSE
+ if(!isliving(user))
+ return FALSE
+ // Torpor?
+ if((check_flags & BP_CANT_USE_IN_TORPOR) && HAS_TRAIT(user, TRAIT_TORPOR))
+ to_chat(user, "Not while you're in Torpor.")
+ return FALSE
+ // Frenzy?
+ if((check_flags & BP_CANT_USE_IN_FRENZY) && vampiredatum_power?.frenzied)
+ to_chat(user, "You cannot use powers while in a Frenzy!")
+ return FALSE
+ // Stake?
+ if((check_flags & BP_CANT_USE_WHILE_STAKED) && vampiredatum_power?.check_staked())
+ to_chat(user, "You have a stake in your chest! Your powers are useless.")
+ return FALSE
+ // Conscious? -- We use our own (AB_CHECK_CONSCIOUS) here so we can control it more, like the error message.
+ if((check_flags & BP_CANT_USE_WHILE_UNCONSCIOUS) && user.stat != CONSCIOUS)
+ to_chat(user, "You can't do this while you are unconcious!")
+ return FALSE
+ // Incapacitated?
+ if((check_flags & BP_CANT_USE_WHILE_INCAPACITATED) && user.incapacitated(IGNORE_RESTRAINTS, IGNORE_GRAB))
+ to_chat(user, "Not while you're incapacitated!")
+ return FALSE
+ // Constant Cost (out of blood)
+ if(constant_bloodcost > 0 && vampiredatum_power?.vampire_blood_volume <= 0)
+ to_chat(user, "You don't have the blood to upkeep [src].")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/UpdateButtonIcon(force = FALSE)
+ background_icon_state = active ? background_icon_state_on : background_icon_state_off
+ . = ..()
+
+/datum/action/cooldown/vampire/proc/pay_cost()
+ // Vassals get powers too!
+ if(!vampiredatum_power)
+ var/mob/living/living_owner = owner
+ if(!HAS_TRAIT(living_owner, TRAIT_NO_BLOOD))
+ living_owner.blood_volume -= bloodcost
+ return
+ // Vampires in a Frenzy don't have enough Blood to pay it, so just don't.
+ if(!vampiredatum_power.frenzied)
+ vampiredatum_power.vampire_blood_volume -= bloodcost
+ vampiredatum_power.update_hud()
+
+/datum/action/cooldown/vampire/proc/ActivatePower(trigger_flags)
+ active = TRUE
+ if(power_flags & BP_AM_TOGGLE)
+ RegisterSignal(owner, COMSIG_LIVING_LIFE, PROC_REF(UsePower))
+
+ owner.log_message("used [src][bloodcost != 0 ? " at the cost of [bloodcost]" : ""].", LOG_ATTACK, color="red")
+ UpdateButtonIcon()
+
+/datum/action/cooldown/vampire/proc/DeactivatePower()
+ if(!active) //Already inactive? Return
+ return
+ if(power_flags & BP_AM_TOGGLE)
+ UnregisterSignal(owner, COMSIG_LIVING_LIFE)
+ if(power_flags & BP_AM_SINGLEUSE)
+ remove_after_use()
+ return
+ active = FALSE
+ StartCooldown()
+ UpdateButtonIcon()
+
+///Used by powers that are continuously active (That have BP_AM_TOGGLE flag)
+/datum/action/cooldown/vampire/proc/UsePower(mob/living/user)
+ if(!ContinueActive(user)) // We can't afford the Power? Deactivate it.
+ DeactivatePower()
+ return FALSE
+ // We can keep this up (For now), so Pay Cost!
+ if(!(power_flags & BP_AM_COSTLESS_UNCONSCIOUS) && user.stat != CONSCIOUS)
+ if(vampiredatum_power)
+ vampiredatum_power.AddBloodVolume(-constant_bloodcost)
+ else
+ var/mob/living/living_user = user
+ if(!HAS_TRAIT(living_user, TRAIT_NO_BLOOD))
+ living_user.blood_volume -= constant_bloodcost
+ return TRUE
+
+/// Checks to make sure this power can stay active
+/datum/action/cooldown/vampire/proc/ContinueActive(mob/living/user, mob/living/target)
+ if(!constant_bloodcost > 0 || vampiredatum_power.vampire_blood_volume > 0)
+ return TRUE
+ return FALSE
+
+/// Used to unlearn Single-Use Powers
+/datum/action/cooldown/vampire/proc/remove_after_use()
+ vampiredatum_power?.powers -= src
+ Remove(owner)
diff --git a/code/modules/antagonists/vampire/powers/cloak.dm b/code/modules/antagonists/vampire/powers/cloak.dm
new file mode 100644
index 0000000000000..3663fdadb857b
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/cloak.dm
@@ -0,0 +1,55 @@
+/datum/action/cooldown/vampire/cloak
+ name = "Cloak of Darkness"
+ desc = "Blend into the shadows and become invisible to the untrained and Artificial eye."
+ button_icon_state = "power_cloak"
+ power_explanation = "\
+ Activate this Power in the shadows and you will turn nearly invisible, scaling with your rank. \
+ Additionally, while Cloak is active, you are completely invisible to the AI."
+ power_flags = BP_AM_TOGGLE
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 5
+ constant_bloodcost = 0.2
+ cooldown_time = 5 SECONDS
+
+/// Must have nobody around to see the cloak
+/datum/action/cooldown/vampire/cloak/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+ for(var/mob/living/watchers in view(9, owner) - owner)
+ owner.balloon_alert(owner, "you can only vanish unseen.")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/cloak/ActivatePower(trigger_flags)
+ . = ..()
+ var/mob/living/user = owner
+ owner.add_movespeed_modifier(/datum/movespeed_modifier/obesity)
+ user.AddElement(/datum/element/digital_camo)
+ user.balloon_alert(user, "cloak turned on.")
+
+/datum/action/cooldown/vampire/cloak/UsePower(seconds_per_tick)
+ . = ..()
+ if(!.)
+ return
+
+ animate(owner, alpha = max(25, owner.alpha - min(75, 10 + 5 * level_current)), time = 1.5 SECONDS)
+
+/datum/action/cooldown/vampire/cloak/ContinueActive(mob/living/user, mob/living/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(user.stat != CONSCIOUS)
+ to_chat(owner, "Your cloak failed because you fell unconcious!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/cloak/DeactivatePower()
+ var/mob/living/user = owner
+
+ animate(user, alpha = 255, time = 1 SECONDS)
+ user.RemoveElement(/datum/element/digital_camo)
+ owner.remove_movespeed_modifier(/datum/movespeed_modifier/obesity)
+ user.balloon_alert(user, "cloak turned off.")
+ return ..()
diff --git a/code/modules/antagonists/vampire/powers/feed.dm b/code/modules/antagonists/vampire/powers/feed.dm
new file mode 100644
index 0000000000000..059265f70e270
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/feed.dm
@@ -0,0 +1,245 @@
+#define FEED_NOTICE_RANGE 2
+#define FEED_DEFAULT_TIMER (10 SECONDS)
+
+/datum/action/cooldown/vampire/feed
+ name = "Feed"
+ desc = "Feed blood off of a living creature."
+ button_icon_state = "power_feed"
+ power_explanation = "\
+ Activate Feed while next to someone and you will begin to feed blood off of them. \
+ The time needed before you start feeding decreases the higher level you are. \
+ Feeding off of someone while you have them aggressively grabbed will put them to sleep. \
+ You are given a Masquerade Infraction if you feed too close to a mortal. \
+ If you are in desperate need of blood, mice can be fed off of, at a cost of your humanity."
+ power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_WHILE_STAKED|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VAMPIRE_DEFAULT_POWER
+ bloodcost = 0
+ cooldown_time = 15 SECONDS
+ ///Amount of blood taken, reset after each Feed. Used for logging.
+ var/blood_taken = 0
+ ///The amount of Blood a target has since our last feed, this loops and lets us not spam alerts of low blood.
+ var/warning_target_bloodvol = BLOOD_VOLUME_MAXIMUM
+ ///Reference to the target we've fed off of
+ var/datum/weakref/target_ref
+ ///Are we feeding with passive grab or not?
+ var/silent_feed = TRUE
+
+/datum/action/cooldown/vampire/feed/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(target_ref) //already sucking blood.
+ return FALSE
+ if(user?.is_mouth_covered() && !isplasmaman(user))
+ owner.balloon_alert(owner, "mouth covered!")
+ return FALSE
+ //Find target, it will alert what the problem is, if any.
+ if(!find_target())
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/feed/ContinueActive(mob/living/user, mob/living/target)
+ if(!target)
+ return FALSE
+ if(!user.Adjacent(target))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/feed/DeactivatePower()
+ var/mob/living/user = owner
+ if(target_ref)
+ var/mob/living/feed_target = target_ref.resolve()
+ log_combat(user, feed_target, "fed on blood", addition="(and took [blood_taken] blood)")
+ to_chat(user, "You slowly release [feed_target].")
+ if(feed_target.stat == DEAD)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankkilled", /datum/mood_event/drankkilled)
+ target_ref = null
+
+ warning_target_bloodvol = BLOOD_VOLUME_MAXIMUM
+ blood_taken = 0
+ REMOVE_TRAIT(user, TRAIT_IMMOBILIZED, TRAIT_FEED)
+ REMOVE_TRAIT(user, TRAIT_MUTE, TRAIT_FEED)
+ return ..()
+
+/datum/action/cooldown/vampire/feed/ActivatePower(trigger_flags)
+ var/mob/living/feed_target = target_ref.resolve()
+ if(istype(feed_target, /mob/living/simple_animal/mouse))
+ to_chat(owner, "You recoil at the taste of a lesser lifeform.")
+ if(vampiredatum_power.my_clan && vampiredatum_power.my_clan.blood_drink_type != VAMPIRE_DRINK_INHUMANELY)
+ var/mob/living/user = owner
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad)
+ vampiredatum_power.AddBloodVolume(25)
+ DeactivatePower()
+ feed_target.death()
+ return
+ var/feed_timer = clamp(round(FEED_DEFAULT_TIMER / (1.25 * (level_current || 1))), 1, FEED_DEFAULT_TIMER)
+ if(vampiredatum_power.frenzied)
+ feed_timer = 2 SECONDS
+
+ owner.balloon_alert(owner, "feeding off [feed_target]...")
+ if(!do_after(owner, feed_timer, feed_target, NONE, TRUE))
+ owner.balloon_alert(owner, "feed stopped")
+ DeactivatePower()
+ return
+ if(owner.pulling == feed_target && owner.grab_state >= GRAB_AGGRESSIVE)
+ if(!IS_VAMPIRE(feed_target) && !IS_VASSAL(feed_target) && !IS_CURATOR(feed_target))
+ feed_target.Unconscious((5 + level_current) SECONDS)
+ if(!feed_target.density)
+ feed_target.Move(owner.loc)
+ owner.visible_message(
+ "[owner] closes [owner.p_their()] mouth around [feed_target]'s neck!",
+ "You sink your fangs into [feed_target]'s neck."
+ )
+ silent_feed = FALSE //no more mr nice guy
+ else
+ // Only people who AREN'T the target will notice this action.
+ var/dead_message = feed_target.stat != DEAD ? " [feed_target.p_they(TRUE)] looks dazed, and will not remember this." : ""
+ owner.visible_message(
+ "[owner] puts [feed_target]'s wrist up to [owner.p_their()] mouth.", \
+ "You slip your fangs into [feed_target]'s wrist.[dead_message]", \
+ vision_distance = FEED_NOTICE_RANGE, ignored_mobs = feed_target)
+
+ //check if we were seen
+ for(var/mob/living/watcher in oviewers(FEED_NOTICE_RANGE) - feed_target)
+ if(!watcher.client)
+ continue
+ if(watcher.has_unlimited_silicon_privilege)
+ continue
+ if(watcher.stat >= DEAD)
+ continue
+ if(watcher.is_blind() || HAS_TRAIT(watcher, TRAIT_NEARSIGHT))
+ continue
+ if(IS_VAMPIRE(watcher) || IS_VASSAL(watcher))
+ continue
+ owner.balloon_alert(owner, "feed noticed!")
+ vampiredatum_power.give_masquerade_infraction()
+ break
+
+ ADD_TRAIT(owner, TRAIT_MUTE, TRAIT_FEED)
+ ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_FEED)
+ return ..()
+
+/datum/action/cooldown/vampire/feed/UsePower(seconds_per_tick)
+ if(!active) //If we aren't active (running on SSfastprocess)
+ return ..() //Manage our cooldown timers
+ var/mob/living/user = owner
+ var/mob/living/feed_target = target_ref.resolve()
+ if(!ContinueActive(user, feed_target))
+ if(!silent_feed)
+ user.visible_message(
+ "[user] is ripped from [feed_target]'s throat. [feed_target.p_their(TRUE)] blood sprays everywhere!",
+ "Your teeth are ripped from [feed_target]'s throat. [feed_target.p_their(TRUE)] blood sprays everywhere!",
+ )
+ // Deal Damage to Target (should have been more careful!)
+ if(iscarbon(feed_target))
+ var/mob/living/carbon/carbon_target = feed_target
+ carbon_target.bleed(15)
+ playsound(get_turf(feed_target), 'sound/effects/splat.ogg', 40, TRUE)
+ if(ishuman(feed_target))
+ var/mob/living/carbon/human/target_user = feed_target
+ target_user.add_bleeding(BLEED_CRITICAL)
+ feed_target.add_splatter_floor(get_turf(feed_target))
+ user.add_mob_blood(feed_target) // Put target's blood on us. The donor goes in the ( )
+ feed_target.add_mob_blood(feed_target)
+ feed_target.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
+ INVOKE_ASYNC(feed_target, TYPE_PROC_REF(/mob, emote), "scream")
+ DeactivatePower()
+ return
+
+ var/feed_strength_mult = 0
+ if(vampiredatum_power.frenzied)
+ feed_strength_mult = 2
+ else if(owner.pulling == feed_target && owner.grab_state >= GRAB_AGGRESSIVE)
+ feed_strength_mult = 1
+ else
+ feed_strength_mult = 0.3
+ blood_taken += vampiredatum_power.handle_feeding(feed_target, feed_strength_mult, level_current)
+
+ if(feed_strength_mult > 5 && feed_target.stat < DEAD)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood)
+ // Drank mindless as Ventrue? - BAD
+ if((vampiredatum_power.my_clan?.blood_drink_type == VAMPIRE_DRINK_SNOBBY) && !feed_target.mind)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad)
+ if(feed_target.stat >= DEAD)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad)
+
+ if(!IS_VAMPIRE(feed_target))
+ if(feed_target.blood_volume <= BLOOD_VOLUME_BAD && warning_target_bloodvol > BLOOD_VOLUME_BAD)
+ owner.balloon_alert(owner, "your victim's blood is fatally low!")
+ else if(feed_target.blood_volume <= BLOOD_VOLUME_OKAY && warning_target_bloodvol > BLOOD_VOLUME_OKAY)
+ owner.balloon_alert(owner, "your victim's blood is dangerously low.")
+ else if(feed_target.blood_volume <= BLOOD_VOLUME_SAFE && warning_target_bloodvol > BLOOD_VOLUME_SAFE)
+ owner.balloon_alert(owner, "your victim's blood is at an unsafe level.")
+ warning_target_bloodvol = feed_target.blood_volume
+
+ if(vampiredatum_power.vampire_blood_volume >= vampiredatum_power.max_blood_volume)
+ user.balloon_alert(owner, "full on blood!")
+ DeactivatePower()
+ return
+ if(feed_target.blood_volume <= 0)
+ user.balloon_alert(owner, "no blood left!")
+ DeactivatePower()
+ return
+ owner.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE)
+ //play sound to target to show they're dying.
+ if(owner.pulling == feed_target && owner.grab_state >= GRAB_AGGRESSIVE)
+ feed_target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE)
+
+/datum/action/cooldown/vampire/feed/proc/find_target()
+ if(owner.pulling && isliving(owner.pulling))
+ if(!can_feed_from(owner.pulling, give_warnings = TRUE))
+ return FALSE
+ target_ref = WEAKREF(owner.pulling)
+ return TRUE
+
+ var/list/close_living_mobs = list()
+ var/list/close_dead_mobs = list()
+ for(var/mob/living/near_targets in oview(1, owner))
+ if(!owner.Adjacent(near_targets))
+ continue
+ if(near_targets.stat)
+ close_living_mobs |= near_targets
+ else
+ close_dead_mobs |= near_targets
+ //Check living first
+ for(var/mob/living/suckers in close_living_mobs)
+ if(can_feed_from(suckers))
+ target_ref = WEAKREF(suckers)
+ return TRUE
+ //If not, check dead
+ for(var/mob/living/suckers in close_dead_mobs)
+ if(can_feed_from(suckers))
+ target_ref = WEAKREF(suckers)
+ return TRUE
+ //No one to suck blood from.
+ return FALSE
+
+/datum/action/cooldown/vampire/feed/proc/can_feed_from(mob/living/target, give_warnings = FALSE)
+ if(istype(target, /mob/living/simple_animal/mouse))
+ if(vampiredatum_power.my_clan?.blood_drink_type == VAMPIRE_DRINK_SNOBBY)
+ if(give_warnings)
+ owner.balloon_alert(owner, "too disgusting!")
+ return FALSE
+ return TRUE
+ //Mice check done, only humans are otherwise allowed
+ if(!ishuman(target))
+ return FALSE
+
+ var/mob/living/carbon/human/target_user = target
+ if(!(target_user.dna?.species) || !(target_user.mob_biotypes & MOB_ORGANIC))
+ if(give_warnings)
+ owner.balloon_alert(owner, "no blood!")
+ return FALSE
+ if(!target_user.can_inject(owner, BODY_ZONE_HEAD, (1 << 0)))
+ if(give_warnings)
+ owner.balloon_alert(owner, "suit too thick!")
+ return FALSE
+ if((vampiredatum_power.my_clan && vampiredatum_power.my_clan.blood_drink_type == VAMPIRE_DRINK_SNOBBY) && !target_user.mind && !vampiredatum_power.frenzied)
+ if(give_warnings)
+ owner.balloon_alert(owner, "cant drink from mindless!")
+ return FALSE
+ return TRUE
+
+#undef FEED_NOTICE_RANGE
+#undef FEED_DEFAULT_TIMER
diff --git a/code/modules/antagonists/vampire/powers/fortitude.dm b/code/modules/antagonists/vampire/powers/fortitude.dm
new file mode 100644
index 0000000000000..45440b6cc09c7
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/fortitude.dm
@@ -0,0 +1,65 @@
+/datum/action/cooldown/vampire/fortitude
+ name = "Fortitude"
+ desc = "Withstand egregious physical wounds and walk away from attacks that would stun, pierce, and dismember lesser beings."
+ button_icon_state = "power_fortitude"
+ power_explanation = "\
+ Activating Fortitude will provide pierce, dismember, and push immunity. \
+ You will additionally gain Brute and Stamina resistance, scaling with your rank. \
+ At level 4, you gain complete stun immunity."
+ power_flags = BP_AM_TOGGLE|BP_AM_COSTLESS_UNCONSCIOUS
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 30
+ cooldown_time = 8 SECONDS
+ constant_bloodcost = 0.2
+ var/fortitude_resist // So we can raise and lower your brute resist based on what your level_current WAS.
+
+/datum/action/cooldown/vampire/fortitude/ActivatePower(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+
+ owner.balloon_alert(owner, "fortitude turned on.")
+ to_chat(owner, "Your flesh has become as hard as steel!")
+ // Traits & Effects
+ ADD_TRAIT(owner, TRAIT_PIERCEIMMUNE, TRAIT_VAMPIRE)
+ ADD_TRAIT(owner, TRAIT_NODISMEMBER, TRAIT_VAMPIRE)
+ ADD_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_VAMPIRE)
+ if(level_current >= 4)
+ ADD_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_VAMPIRE) // They'll get stun resistance + this, who cares.
+ var/mob/living/carbon/human/vampire_user = owner
+ if(IS_VAMPIRE(owner) || IS_VASSAL(owner))
+ fortitude_resist = max(0.3, 0.7 - level_current * 0.1)
+ vampire_user.physiology.brute_mod *= fortitude_resist
+ vampire_user.physiology.stamina_mod *= fortitude_resist
+
+ owner.add_movespeed_modifier(/datum/movespeed_modifier/obesity)
+
+/datum/action/cooldown/vampire/fortitude/UsePower(seconds_per_tick)
+ . = ..()
+ if(!. || !active)
+ return
+
+ /// We don't want people using fortitude being able to use vehicles
+ var/mob/living/carbon/user = owner
+ if(user.buckled && istype(user.buckled, /obj/vehicle))
+ user.buckled.unbuckle_mob(src, force=TRUE)
+
+/datum/action/cooldown/vampire/fortitude/DeactivatePower()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/vampire_user = owner
+ if(IS_VAMPIRE(owner) || IS_VASSAL(owner))
+ vampire_user.physiology.brute_mod /= fortitude_resist
+ if(!HAS_TRAIT_FROM(vampire_user, TRAIT_STUNIMMUNE, TRAIT_VAMPIRE))
+ vampire_user.physiology.stamina_mod /= fortitude_resist
+ // Remove Traits & Effects
+ REMOVE_TRAIT(owner, TRAIT_PIERCEIMMUNE, TRAIT_VAMPIRE)
+ REMOVE_TRAIT(owner, TRAIT_NODISMEMBER, TRAIT_VAMPIRE)
+ REMOVE_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_VAMPIRE)
+ REMOVE_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_VAMPIRE)
+
+ owner.remove_movespeed_modifier(/datum/movespeed_modifier/obesity)
+ owner.balloon_alert(owner, "fortitude turned off.")
+
+ return ..()
diff --git a/code/modules/antagonists/vampire/powers/gohome.dm b/code/modules/antagonists/vampire/powers/gohome.dm
new file mode 100644
index 0000000000000..11896d189b79e
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/gohome.dm
@@ -0,0 +1,120 @@
+#define GOHOME_START 0
+#define GOHOME_FLICKER_ONE 2
+#define GOHOME_FLICKER_TWO 4
+#define GOHOME_TELEPORT 6
+
+/**
+ * Given to Vampires near Sol if they have a Coffin claimed.
+ * Teleports them to their Coffin after a delay.
+ * Makes them drop everything if someone witnesses the act.
+ */
+/datum/action/cooldown/vampire/gohome
+ name = "Vanishing Act"
+ desc = "As dawn aproaches, disperse into mist and return directly to your Lair. WARNING: You will drop ALL of your possessions if observed by mortals."
+ button_icon_state = "power_gohome"
+ power_explanation = "\
+ Activating Vanishing Act will, after a short delay, teleport you to your Claimed Coffin. \
+ Immediately after activating, lights around the user will begin to flicker. \
+ Once the user teleports to their coffin, in their place will be a Rat or Bat."
+ power_flags = BP_AM_TOGGLE|BP_AM_SINGLEUSE|BP_AM_STATIC_COOLDOWN
+ check_flags = BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_STAKED
+ purchase_flags = NONE
+ bloodcost = 100
+ constant_bloodcost = 2
+ cooldown_time = 100 SECONDS
+ ///What stage of the teleportation are we in
+ var/teleporting_stage = GOHOME_START
+ ///The types of mobs that will drop post-teleportation.
+ var/static/list/spawning_mobs = list(
+ /mob/living/simple_animal/mouse = 3,
+ /mob/living/simple_animal/hostile/retaliate/bat = 1,
+ )
+
+/datum/action/cooldown/vampire/gohome/can_use(mob/living/carbon/user, trigger_flags)
+ if(!..())
+ return FALSE
+ /// Have No Lair (NOTE: You only got this power if you had a lair, so this means it's destroyed)
+ if(!istype(vampiredatum_power) || !vampiredatum_power.coffin)
+ owner.balloon_alert(owner, "coffin was destroyed!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/gohome/ActivatePower(trigger_flags)
+ ..()
+ owner.balloon_alert(owner, "preparing to teleport...")
+ if(do_after(owner, GOHOME_TELEPORT SECONDS, timed_action_flags=(IGNORE_USER_LOC_CHANGE | IGNORE_INCAPACITATED | IGNORE_HELD_ITEM)))
+ teleport_to_coffin(owner)
+
+/datum/action/cooldown/vampire/gohome/UsePower(seconds_per_tick)
+ if(!..())
+ return FALSE
+ switch(teleporting_stage)
+ if(GOHOME_START)
+ INVOKE_ASYNC(src, PROC_REF(flicker_lights), 3, 20)
+ if(GOHOME_FLICKER_ONE)
+ INVOKE_ASYNC(src, PROC_REF(flicker_lights), 4, 40)
+ if(GOHOME_FLICKER_TWO)
+ INVOKE_ASYNC(src, PROC_REF(flicker_lights), 4, 60)
+ teleporting_stage++
+
+/datum/action/cooldown/vampire/gohome/ContinueActive(mob/living/user, mob/living/target)
+ if(!..())
+ return FALSE
+ if(!isturf(owner.loc))
+ return FALSE
+ if(!vampiredatum_power.coffin)
+ user.balloon_alert(user, "coffin destroyed!")
+ to_chat(owner, "Your coffin has been destroyed! You no longer have a destination.")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/gohome/proc/flicker_lights(flicker_range, beat_volume)
+ for(var/obj/machinery/light/nearby_lights in view(flicker_range, get_turf(owner)))
+ nearby_lights.flicker(5)
+ playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', beat_volume, 1)
+
+/datum/action/cooldown/vampire/gohome/proc/teleport_to_coffin(mob/living/carbon/user)
+ var/drop_item = FALSE
+ var/turf/current_turf = get_turf(owner)
+ // If we aren't in the dark, anyone watching us will cause us to drop out stuff
+ if(!QDELETED(current_turf?.lighting_object) && current_turf.get_lumcount() >= 0.2)
+ for(var/mob/living/watcher in viewers(world.view, get_turf(owner)) - owner)
+ if(!watcher.client)
+ continue
+ if(watcher.has_unlimited_silicon_privilege)
+ continue
+ if(watcher.is_blind())
+ continue
+ if(!IS_VAMPIRE(watcher) && !IS_VASSAL(watcher))
+ drop_item = TRUE
+ break
+ user.uncuff()
+ if(drop_item)
+ for(var/obj/item/literally_everything in owner)
+ owner.dropItemToGround(literally_everything, TRUE)
+
+ playsound(current_turf, 'sound/magic/summon_karp.ogg', 60, 1)
+
+ var/datum/effect_system/steam_spread/vampire/puff = new /datum/effect_system/steam_spread/vampire()
+ puff.set_up(3, 0, current_turf)
+ puff.start()
+
+ /// STEP FIVE: Create animal at prev location
+ var/mob/living/simple_animal/new_mob = pick_weight(spawning_mobs)
+ new new_mob(current_turf)
+ /// TELEPORT: Move to Coffin & Close it!
+ user.set_resting(TRUE, TRUE, FALSE)
+ do_teleport(owner, vampiredatum_power.coffin, channel = TELEPORT_CHANNEL_MAGIC, bypass_area_restriction = TRUE, no_effects = TRUE)
+ vampiredatum_power.coffin.close(owner)
+ vampiredatum_power.coffin.take_contents()
+ playsound(vampiredatum_power.coffin.loc, vampiredatum_power.coffin.close_sound, 15, 1, -3)
+
+ DeactivatePower()
+
+/datum/effect_system/steam_spread/vampire
+ effect_type = /obj/effect/particle_effect/smoke/vampsmoke
+
+#undef GOHOME_START
+#undef GOHOME_FLICKER_ONE
+#undef GOHOME_FLICKER_TWO
+#undef GOHOME_TELEPORT
diff --git a/code/modules/antagonists/vampire/powers/masquerade.dm b/code/modules/antagonists/vampire/powers/masquerade.dm
new file mode 100644
index 0000000000000..4b6105c21b539
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/masquerade.dm
@@ -0,0 +1,91 @@
+/**
+ * # WITHOUT THIS POWER:
+ *
+ * - Mid-Blood: SHOW AS PALE
+ * - Low-Blood: SHOW AS DEAD
+ * - No Heartbeat
+ * - Examine shows actual blood
+ * - Thermal homeostasis (ColdBlooded)
+ * WITH THIS POWER:
+ * - Normal body temp -- remove Cold Blooded (return on deactivate)
+ */
+
+/datum/action/cooldown/vampire/masquerade
+ name = "Masquerade"
+ desc = "Feign the vital signs of a mortal, and escape both casual and medical notice as the monster you truly are."
+ button_icon_state = "power_human"
+ power_explanation = "\
+ Masquerade will forge your identity to be practically identical to that of a human;\
+ You lose nearly all Vampire benefits, including your passive healing.\
+ You gain a Genetic sequence, and appear to have 100% blood when scanned by a Health Analyzer.\
+ You will not appear as Pale when examined. Anything further than Pale, however, will not be hidden.\
+ After deactivating Masquerade, you will re-gain your Vampiric abilities, as well as lose any Diseases or mutations you might have."
+ power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN|BP_AM_COSTLESS_UNCONSCIOUS
+ check_flags = BP_CANT_USE_IN_FRENZY
+ purchase_flags = VAMPIRE_CAN_BUY|VAMPIRE_DEFAULT_POWER
+ bloodcost = 10
+ cooldown_time = 5 SECONDS
+ constant_bloodcost = 0.1
+
+/datum/action/cooldown/vampire/masquerade/ActivatePower(trigger_flags)
+ . = ..()
+ var/mob/living/carbon/user = owner
+ owner.balloon_alert(owner, "masquerade turned on.")
+ to_chat(user, "Your heart beats falsely within your lifeless chest. You may yet pass for a mortal.")
+ to_chat(user, "Your vampiric healing is halted while imitating life.")
+
+ // Give status effect
+ user.apply_status_effect(/datum/status_effect/masquerade)
+
+ // Handle Traits
+ user.remove_traits(vampiredatum_power.vampire_traits, TRAIT_VAMPIRE)
+ ADD_TRAIT(user, TRAIT_MASQUERADE, TRAIT_VAMPIRE)
+ // Handle organs
+ var/obj/item/organ/heart/vampheart = user.getorgan(/obj/item/organ/heart)
+ vampheart?.Restart()
+ var/obj/item/organ/eyes/eyes = user.getorgan(/obj/item/organ/eyes)
+ eyes?.flash_protect = initial(eyes.flash_protect)
+
+/datum/action/cooldown/vampire/masquerade/DeactivatePower()
+ . = ..()
+ var/mob/living/carbon/user = owner
+ owner.balloon_alert(owner, "masquerade turned off.")
+
+ // Remove status effect, mutations & diseases that you got while on masq.
+ user.remove_status_effect(/datum/status_effect/masquerade)
+ user.dna.remove_all_mutations()
+ for(var/datum/disease/diseases as anything in user.diseases)
+ diseases.cure()
+
+ // Handle Traits
+ user.add_traits(vampiredatum_power.vampire_traits, TRAIT_VAMPIRE)
+ REMOVE_TRAIT(user, TRAIT_MASQUERADE, TRAIT_VAMPIRE)
+
+ // Handle organs
+ var/obj/item/organ/heart/vampheart = user.getorganslot(ORGAN_SLOT_HEART)
+ vampheart?.Stop()
+ var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
+ eyes?.flash_protect = max(initial(eyes.flash_protect) - 1, - 1)
+ to_chat(user, "Your heart beats one final time, while your skin dries out and your icy pallor returns.")
+
+/**
+ * # Status effect
+ *
+ * This is what the Masquerade power gives, handles their bonuses and gives them a neat icon to tell them they're on Masquerade.
+ */
+
+/datum/status_effect/masquerade
+ id = "masquerade"
+ duration = -1
+ tick_interval = -1
+ alert_type = /atom/movable/screen/alert/status_effect/masquerade
+
+/atom/movable/screen/alert/status_effect/masquerade
+ name = "Masquerade"
+ desc = "You are currently hiding your identity using the Masquerade power. This halts Vampiric healing."
+ icon_state = "masquerade_active"
+ alerttooltipstyle = "cult"
+
+/atom/movable/screen/alert/status_effect/masquerade/MouseEntered(location,control,params)
+ desc = initial(desc)
+ return ..()
diff --git a/code/modules/antagonists/vampire/powers/targeted/_targeted.dm b/code/modules/antagonists/vampire/powers/targeted/_targeted.dm
new file mode 100644
index 0000000000000..26a6e2d51cd05
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/targeted/_targeted.dm
@@ -0,0 +1,104 @@
+// NOTE: All Targeted spells are Toggles! We just don't bother checking here.
+/datum/action/cooldown/vampire/targeted
+ power_flags = BP_AM_TOGGLE
+
+ ///If set, how far the target has to be for the power to work.
+ var/target_range
+ ///Message sent to chat when clicking on the power, before you use it.
+ var/prefire_message
+ ///Most powers happen the moment you click. Some, like Mesmerize, require time and shouldn't cost you if they fail.
+ var/power_activates_immediately = TRUE
+ ///Is this power LOCKED due to being used?
+ var/power_in_use = FALSE
+
+/// Modify description to add notice that this is aimed.
+/datum/action/cooldown/vampire/targeted/New(Target)
+ desc += " \[Targeted Power\]"
+ return ..()
+
+/datum/action/cooldown/vampire/targeted/Remove(mob/living/remove_from)
+ . = ..()
+ if(remove_from.click_intercept == src)
+ unset_click_ability(remove_from)
+
+/datum/action/cooldown/vampire/targeted/Trigger(trigger_flags, atom/target)
+ if(active)
+ DeactivatePower()
+ return FALSE
+ if(!can_pay_cost(owner) || !can_use(owner, trigger_flags))
+ return FALSE
+
+ if(prefire_message)
+ to_chat(owner, "[prefire_message]")
+
+ ActivatePower(trigger_flags)
+ if(target)
+ return InterceptClickOn(owner, null, target)
+
+ return set_click_ability(owner)
+
+/datum/action/cooldown/vampire/targeted/DeactivatePower()
+ if(power_flags & BP_AM_TOGGLE)
+ UnregisterSignal(owner, COMSIG_LIVING_LIFE)
+ active = FALSE
+ UpdateButtonIcon()
+ unset_click_ability(owner)
+
+/// Check if target is VALID (wall, turf, or character?)
+/datum/action/cooldown/vampire/targeted/proc/CheckValidTarget(atom/target_atom)
+ return !(target_atom == owner)
+
+/// Check if valid target meets conditions
+/datum/action/cooldown/vampire/targeted/proc/CheckCanTarget(atom/target_atom)
+ if(target_range)
+ // Out of Range
+ if(!(target_atom in view(target_range, owner)))
+ if(target_range > 1) // Only warn for range if it's greater than 1. Brawn doesn't need to announce itself.
+ owner.balloon_alert(owner, "out of range.")
+ return FALSE
+ return istype(target_atom)
+
+/datum/action/cooldown/vampire/targeted/proc/unset_click_ability(mob/on_who, refund_cooldown = TRUE)
+ owner.client.mouse_pointer_icon = null
+ SHOULD_CALL_PARENT(TRUE)
+
+ on_who.click_intercept = null
+ UpdateButtonIcon()
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/proc/set_click_ability(mob/on_who)
+ owner.client.mouse_pointer_icon = 'icons/effects/cult_target.dmi'
+ SHOULD_CALL_PARENT(TRUE)
+
+ on_who.click_intercept = src
+ UpdateButtonIcon()
+ return TRUE
+
+/// Click Target
+/datum/action/cooldown/vampire/targeted/proc/click_with_power(atom/target_atom)
+ // CANCEL RANGED TARGET check
+ if(power_in_use || !CheckValidTarget(target_atom))
+ return FALSE
+ // Valid? (return true means DON'T cancel power!)
+ if(!can_pay_cost() || !can_use(owner) || !CheckCanTarget(target_atom))
+ return TRUE
+ power_in_use = TRUE // Lock us into this ability until it successfully fires off. Otherwise, we pay the blood even if we fail.
+ FireTargetedPower(target_atom) // We use this instead of ActivatePower(trigger_flags), which has no input
+ // Skip this part so we can return TRUE right away.
+ if(power_activates_immediately)
+ power_activated_sucessfully() // Mesmerize pays only after success.
+ power_in_use = FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/proc/FireTargetedPower(atom/target_atom)
+ log_combat(owner, target_atom, "used [name] on")
+
+/// The power went off! We now pay the cost of the power.
+/datum/action/cooldown/vampire/targeted/proc/power_activated_sucessfully()
+ unset_click_ability(owner)
+ pay_cost()
+ StartCooldown()
+ DeactivatePower()
+
+/datum/action/cooldown/vampire/targeted/proc/InterceptClickOn(mob/living/caller, params, atom/target)
+ click_with_power(target)
diff --git a/code/modules/antagonists/vampire/powers/targeted/brawn.dm b/code/modules/antagonists/vampire/powers/targeted/brawn.dm
new file mode 100644
index 0000000000000..f58bb5ac3b324
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/targeted/brawn.dm
@@ -0,0 +1,176 @@
+/datum/action/cooldown/vampire/targeted/brawn
+ name = "Brawn"
+ desc = "Snap restraints, break lockers and doors, or deal terrible damage with your bare hands."
+ button_icon_state = "power_strength"
+ power_explanation = "\
+ Click any person to bash into them, break restraints, or knock your grabber down. Only one of these can be done per use. \
+ Punching a Cyborg will EMP it and deal high damage. \
+ At level 3, you can break closets open, and additionally you can break restraints. \
+ At level 4, you can bash airlocks open. \
+ Higher ranks will increase the damage when punching someone."
+ power_flags = BP_AM_TOGGLE
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 8
+ cooldown_time = 9 SECONDS
+ target_range = 1
+ power_activates_immediately = TRUE
+ prefire_message = "Select a target."
+
+/datum/action/cooldown/vampire/targeted/brawn/ActivatePower(trigger_flags)
+ // Did we break out of our handcuffs?
+ if(break_restraints())
+ power_activated_sucessfully()
+ return FALSE
+ // Did we knock a grabber down? We can only do this while not also breaking restraints if strong enough.
+ if(level_current >= 3 && escape_puller())
+ power_activated_sucessfully()
+ return FALSE
+ // Did neither, now we can PUNCH.
+ return ..()
+
+// Look at 'biodegrade.dm' for reference
+/datum/action/cooldown/vampire/targeted/brawn/proc/break_restraints()
+ var/mob/living/carbon/human/user = owner
+ ///Only one form of shackles removed per use
+ var/used = FALSE
+
+ // Breaks out of lockers
+ if(istype(user.loc, /obj/structure/closet))
+ var/obj/structure/closet/closet = user.loc
+ if(!istype(closet))
+ return FALSE
+ closet.visible_message(
+ "[closet] tears apart as [user] bashes it open from within!",
+ "[closet] tears apart as you bash it open from within!",
+ )
+ to_chat(user, "We bash [closet] wide open!")
+ addtimer(CALLBACK(src, PROC_REF(break_closet), user, closet), 1)
+ used = TRUE
+
+ if(!used)
+ user.uncuff()
+ user.visible_message(
+ "[user] discards their restraints like it's nothing!",
+ "We break through our restraints!",
+ )
+ used = TRUE
+
+ // Remove Straightjackets
+ if(user.wear_suit?.breakouttime && !used)
+ var/obj/item/clothing/suit/straightjacket = user.get_item_by_slot(ITEM_SLOT_OCLOTHING)
+ user.visible_message(
+ "[user] rips straight through the [user.p_their()] [straightjacket]!",
+ "We tear through our [straightjacket]!",
+ )
+ if(straightjacket && user.wear_suit == straightjacket)
+ qdel(straightjacket)
+ used = TRUE
+
+ // Did we end up using our ability? If so, play the sound effect and return TRUE
+ if(used)
+ playsound(get_turf(user), 'sound/effects/grillehit.ogg', 80, 1, -1)
+ return used
+
+// This is its own proc because its done twice, to repeat code copypaste.
+/datum/action/cooldown/vampire/targeted/brawn/proc/break_closet(mob/living/carbon/human/user, obj/structure/closet/closet)
+ if(closet)
+ closet.welded = FALSE
+ closet.locked = FALSE
+ closet.broken = TRUE
+ closet.open()
+
+/datum/action/cooldown/vampire/targeted/brawn/proc/escape_puller()
+ if(!owner.pulledby) // || owner.pulledby.grab_state <= GRAB_PASSIVE)
+ return FALSE
+ var/mob/pulled_mob = owner.pulledby
+ var/pull_power = pulled_mob.grab_state
+ playsound(get_turf(pulled_mob), 'sound/effects/woodhit.ogg', 75, 1, -1)
+ // Knock Down (if Living)
+ if(isliving(pulled_mob))
+ var/mob/living/hit_target = pulled_mob
+ hit_target.Knockdown(pull_power * 10 + 20)
+ // Knock Back (before Knockdown, which probably cancels pull)
+ var/send_dir = get_dir(owner, pulled_mob)
+ var/turf/turf_thrown_at = get_ranged_target_turf(pulled_mob, send_dir, pull_power)
+ owner.newtonian_move(send_dir) // Bounce back in 0 G
+ pulled_mob.throw_at(turf_thrown_at, pull_power, TRUE, owner, FALSE) // Throw distance based on grab state! Harder grabs punished more aggressively.
+ log_combat(owner, pulled_mob, "used Brawn power")
+ owner.visible_message(
+ "[owner] tears free of [pulled_mob]'s grasp!",
+ "You shrug off [pulled_mob]'s grasp!",
+ )
+ owner.pulledby = null // It's already done, but JUST IN CASE.
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/brawn/FireTargetedPower(atom/target_atom)
+ . = ..()
+ var/mob/living/user = owner
+
+ // Living Targets
+ if(isliving(target_atom))
+ var/mob/living/target = target_atom
+ var/mob/living/carbon/carbonuser = user
+ var/hitStrength = carbonuser.dna.species.punchdamage * 1.25 + 2
+ // Knockdown!
+ var/powerlevel = min(5, 1 + level_current)
+ if(rand(5 + powerlevel) >= 5)
+ target.visible_message(
+ "[user] lands a vicious punch, sending [target] away!", \
+ "[user] has landed a horrifying punch on you and sends you flying!",
+ )
+ target.Knockdown(min(5, rand(10, 10 * powerlevel)))
+ // Attack!
+ owner.balloon_alert(owner, "you punch [target]!")
+ playsound(get_turf(target), 'sound/weapons/punch4.ogg', 60, 1, -1)
+ user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
+ var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(target.get_combat_bodyzone()))
+ target.apply_damage(hitStrength, BRUTE, affecting)
+ // Knockback
+ var/send_dir = get_dir(owner, target)
+ var/turf/turf_thrown_at = get_ranged_target_turf(target, send_dir, powerlevel)
+ owner.newtonian_move(send_dir) // Bounce back in 0 G
+ target.throw_at(turf_thrown_at, powerlevel, TRUE, owner)
+ // Target Type: Cyborg (Also gets the effects above)
+ if(issilicon(target))
+ target.emp_act(EMP_HEAVY)
+ // Lockers
+ else if(istype(target_atom, /obj/structure/closet) && level_current >= 3)
+ var/obj/structure/closet/target_closet = target_atom
+ user.balloon_alert(user, "you prepare to bash [target_closet] open...")
+ if(!do_after(user, 2.5 SECONDS, target_closet))
+ user.balloon_alert(user, "interrupted!")
+ return FALSE
+ target_closet.visible_message("[target_closet] breaks open as [user] bashes it!")
+ addtimer(CALLBACK(src, PROC_REF(break_closet), user, target_closet), 1)
+ playsound(get_turf(user), 'sound/effects/grillehit.ogg', 80, TRUE, -1)
+ // Airlocks
+ else if(istype(target_atom, /obj/machinery/door) && level_current >= 4)
+ var/obj/machinery/door/target_airlock = target_atom
+ playsound(get_turf(user), 'sound/machines/airlock_alien_prying.ogg', 40, TRUE, -1)
+ owner.balloon_alert(owner, "you prepare to tear open [target_airlock]...")
+ if(!do_after(user, 2.5 SECONDS, target_airlock))
+ user.balloon_alert(user, "interrupted!")
+ return FALSE
+ if(target_airlock.Adjacent(user))
+ target_airlock.visible_message("[target_airlock] breaks open as [user] bashes it!")
+ user.Stun(10)
+ user.do_attack_animation(target_airlock, ATTACK_EFFECT_SMASH)
+ playsound(get_turf(target_airlock), 'sound/effects/bang.ogg', 30, 1, -1)
+ target_airlock.open(2) // open(2) is like a crowbar or jaws of life.
+
+/datum/action/cooldown/vampire/targeted/brawn/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ return isliving(target_atom) || istype(target_atom, /obj/machinery/door) || istype(target_atom, /obj/structure/closet)
+
+/datum/action/cooldown/vampire/targeted/brawn/CheckCanTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ // Can't be in a locker when targeting someone
+ if(istype(owner.loc, /obj/structure/closet))
+ return FALSE
+ return isliving(target_atom) || istype(target_atom, /obj/machinery/door) || istype(target_atom, /obj/structure/closet)
diff --git a/code/modules/antagonists/vampire/powers/targeted/haste.dm b/code/modules/antagonists/vampire/powers/targeted/haste.dm
new file mode 100644
index 0000000000000..c9c71c3961905
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/targeted/haste.dm
@@ -0,0 +1,89 @@
+/* Level 1: Speed to location
+ * Level 2: Dodge Bullets
+ * Level 3: Stun People Passed
+ */
+
+/datum/action/cooldown/vampire/targeted/haste
+ name = "Immortal Haste"
+ desc = "Dash somewhere with supernatural speed. Those nearby may be knocked away, stunned, or left empty-handed."
+ button_icon_state = "power_speed"
+ power_explanation = "\
+ Click anywhere to immediately dash towards that location. \
+ The Power will not work if you are lying down, in no gravity, or are aggressively grabbed. \
+ Anyone in your way during your Haste will be knocked down. \
+ Higher levels will increase the knockdown dealt to enemies."
+ power_flags = BP_AM_TOGGLE
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 6
+ cooldown_time = 12 SECONDS
+ target_range = 15
+ power_activates_immediately = TRUE
+ ///List of all people hit by our power, so we don't hit them again.
+ var/list/hit = list()
+
+/datum/action/cooldown/vampire/targeted/haste/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+ // Being Grabbed
+ if(user.pulledby && user.pulledby.grab_state >= GRAB_AGGRESSIVE)
+ user.balloon_alert(user, "you're being grabbed!")
+ return FALSE
+ if(!user.has_gravity(user.loc)) //We dont want people to be able to use this to fly around in space
+ user.balloon_alert(user, "you cannot dash while floating!")
+ return FALSE
+ if(user.body_position == LYING_DOWN)
+ user.balloon_alert(user, "you must be standing to tackle!")
+ return FALSE
+ return TRUE
+
+/// Anything will do, if it's not me or my square
+/datum/action/cooldown/vampire/targeted/haste/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ return target_atom.loc != owner.loc
+
+/// This is a non-async proc to make sure the power is "locked" until this finishes.
+/datum/action/cooldown/vampire/targeted/haste/FireTargetedPower(atom/target_atom)
+ . = ..()
+ RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+ var/mob/living/user = owner
+ var/turf/targeted_turf = isturf(target_atom) ? target_atom : get_turf(target_atom)
+ // Pulled? Not anymore.
+ user.pulledby?.stop_pulling()
+ // Go to target turf
+ // DO NOT USE WALK TO.
+ owner.balloon_alert(owner, "you dash into the air!")
+ playsound(get_turf(owner), 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+ var/safety = get_dist(user, targeted_turf) * 3 + 1
+ var/consequetive_failures = 0
+ while(--safety && (get_turf(user) != targeted_turf))
+ var/success = step_towards(user, targeted_turf) //This does not try to go around obstacles.
+ if(!success)
+ success = step_to(user, targeted_turf) //this does
+ if(!success)
+ consequetive_failures++
+ if(consequetive_failures >= 3) //if 3 steps don't work
+ break //just stop
+ else
+ consequetive_failures = 0 //reset so we can keep moving
+ if(user.resting || user.incapacitated(IGNORE_RESTRAINTS, IGNORE_GRAB)) //actually down? stop.
+ break
+ if(success) //don't sleep if we failed to move.
+ sleep(world.tick_lag)
+
+/datum/action/cooldown/vampire/targeted/haste/power_activated_sucessfully()
+ . = ..()
+ UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
+ hit.Cut()
+
+/datum/action/cooldown/vampire/targeted/haste/proc/on_move()
+ for(var/mob/living/hit_living in dview(1, get_turf(owner)) - owner)
+ if(hit.Find(hit_living))
+ continue
+ hit += hit_living
+ playsound(hit_living, "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
+ hit_living.Knockdown(10 + level_current * 4)
+ hit_living.spin(10, 1)
diff --git a/code/modules/antagonists/vampire/powers/targeted/lunge.dm b/code/modules/antagonists/vampire/powers/targeted/lunge.dm
new file mode 100644
index 0000000000000..2b5545a87ecdd
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/targeted/lunge.dm
@@ -0,0 +1,161 @@
+/datum/action/cooldown/vampire/targeted/lunge
+ name = "Predatory Lunge"
+ desc = "Spring at your target to grapple them without warning, or tear the dead's heart out. Attacks from concealment or the rear may even knock them down if strong enough."
+ button_icon_state = "power_lunge"
+ power_explanation = "Predatory Lunge:\n\
+ Click any player to start spinning wildly and, after a short delay, lunge at them. \
+ When lunging at someone, you will aggressively grab them, unless they are a curator. \
+ You cannot use Lunge if you are already grabbing someone, or are being grabbed. \
+ If you grab from behind or darkness, you will knock the target down, scaling with your rank. \
+ If used on a dead body, you will tear their organs out. \
+ At level 4, you will instantly lunge, but are limited to tackling from only 6 tiles away."
+ power_flags = NONE
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 10
+ cooldown_time = 10 SECONDS
+ power_activates_immediately = FALSE
+
+/datum/action/cooldown/vampire/targeted/lunge/upgrade_power()
+ . = ..()
+ //range is lowered when you get stronger.
+ if(level_current > 3)
+ target_range = 6
+
+/datum/action/cooldown/vampire/targeted/lunge/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(user.pulledby && user.pulledby.grab_state >= GRAB_AGGRESSIVE)
+ owner.balloon_alert(user, "grabbed!")
+ return FALSE
+ if(user.pulling)
+ owner.balloon_alert(user, "grabbing someone!")
+ return FALSE
+ if(datum_flags & DF_ISPROCESSING)
+ owner.balloon_alert(user, "already lunging!")
+ return FALSE
+ return TRUE
+
+/// Check: Are we lunging at a person?
+/datum/action/cooldown/vampire/targeted/lunge/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ return isliving(target_atom)
+
+/datum/action/cooldown/vampire/targeted/lunge/CheckCanTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!isturf(target_atom.loc))
+ return FALSE
+ // Check: can the Vampire even move?
+ var/mob/living/user = owner
+ if(user.body_position == LYING_DOWN || HAS_TRAIT(owner, TRAIT_IMMOBILIZED))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/lunge/FireTargetedPower(atom/target_atom)
+ . = ..()
+ owner.face_atom(target_atom)
+ if(level_current > 3)
+ do_lunge(target_atom)
+ return
+
+ prepare_target_lunge(target_atom)
+ return TRUE
+
+///Starts processing the power and prepares the lunge by spinning, calls lunge at the end of it.
+/datum/action/cooldown/vampire/targeted/lunge/proc/prepare_target_lunge(atom/target_atom)
+ START_PROCESSING(SSprocessing, src)
+ owner.balloon_alert(owner, "lunge started!")
+ //animate them shake
+ var/base_x = owner.base_pixel_x
+ var/base_y = owner.base_pixel_y
+ animate(owner, pixel_x = base_x, pixel_y = base_y, time = 1, loop = -1)
+ for(var/i in 1 to 25)
+ var/x_offset = base_x + rand(-3, 3)
+ var/y_offset = base_y + rand(-3, 3)
+ animate(pixel_x = x_offset, pixel_y = y_offset, time = 1)
+
+ if(!do_after(owner, 4 SECONDS, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE), extra_checks = CALLBACK(src, PROC_REF(CheckCanTarget), target_atom)))
+ end_target_lunge(base_x, base_y)
+
+ return FALSE
+
+ end_target_lunge()
+ do_lunge(target_atom)
+ return TRUE
+
+///When preparing to lunge ends, this clears it up.
+/datum/action/cooldown/vampire/targeted/lunge/proc/end_target_lunge(base_x, base_y)
+ animate(owner, pixel_x = base_x, pixel_y = base_y, time = 1)
+ STOP_PROCESSING(SSprocessing, src)
+
+/datum/action/cooldown/vampire/targeted/lunge/process()
+ if(!active) //If running SSfasprocess (on cooldown)
+ return ..() //Manage our cooldown timers
+ if(prob(75))
+ owner.spin(8, 1)
+ owner.balloon_alert_to_viewers("spins wildly!", "you spin!")
+ return
+ do_smoke(0, owner.loc, smoke_type = /obj/effect/particle_effect/smoke/transparent)
+
+///Actually lunges the target, then calls lunge end.
+/datum/action/cooldown/vampire/targeted/lunge/proc/do_lunge(atom/hit_atom)
+ var/turf/targeted_turf = get_turf(hit_atom)
+
+ var/safety = get_dist(owner, targeted_turf) * 3 + 1
+ var/consequetive_failures = 0
+ while(--safety && !hit_atom.Adjacent(owner))
+ if(!step_to(owner, targeted_turf))
+ consequetive_failures++
+ if(consequetive_failures >= 3) // If 3 steps don't work, just stop.
+ break
+
+ lunge_end(hit_atom, targeted_turf)
+
+/datum/action/cooldown/vampire/targeted/lunge/proc/lunge_end(atom/hit_atom, turf/target_turf)
+ power_activated_sucessfully()
+ // Am I next to my target to start giving the effects?
+ if(!owner.Adjacent(hit_atom))
+ return
+
+ var/mob/living/user = owner
+ var/mob/living/carbon/target = hit_atom
+
+ // Did I slip or get knocked unconscious?
+ if(user.body_position != STANDING_UP || user.incapacitated())
+ var/send_dir = get_dir(user, target_turf)
+ new /datum/forced_movement(user, get_ranged_target_turf(user, send_dir, 1), 1, FALSE)
+ user.spin(10)
+ return
+
+ if(IS_CURATOR(target) || target.is_shove_knockdown_blocked())
+ owner.balloon_alert(owner, "pushed away!")
+ target.grabbedby(owner)
+ return
+
+ owner.balloon_alert(owner, "you lunge at [target]!")
+ if(target.stat == DEAD)
+ playsound(get_turf(target), 'sound/effects/splat.ogg', 40, TRUE)
+ owner.visible_message(
+ "[owner] tears into [target]'s chest!",
+ "You tear into [target]'s chest!",
+ )
+ var/obj/item/bodypart/chest/chest = target.get_bodypart(BODY_ZONE_CHEST)
+ chest.dismember()
+ else
+ target.grabbedby(owner)
+ target.grippedby(owner, instant = TRUE)
+ // Did we knock them down?
+ if(!is_source_facing_target(target, owner) || owner.alpha <= 40)
+ target.Knockdown(10 + level_current * 5)
+ target.Paralyze(0.1)
+
+/datum/action/cooldown/vampire/targeted/lunge/DeactivatePower()
+ REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_VAMPIRE)
+ return ..()
diff --git a/code/modules/antagonists/vampire/powers/targeted/mesmerize.dm b/code/modules/antagonists/vampire/powers/targeted/mesmerize.dm
new file mode 100644
index 0000000000000..ed4aebfb72bd1
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/targeted/mesmerize.dm
@@ -0,0 +1,139 @@
+/**
+ * MEZMERIZE
+ * Locks a target in place for a certain amount of time.
+ *
+ * Level 2: Additionally mutes
+ * Level 3: Can be used through face protection
+ * Level 5: Doesn't need to be facing you anymore
+ */
+
+/datum/action/cooldown/vampire/targeted/mesmerize
+ name = "Mesmerize"
+ desc = "Dominate the mind of a mortal who can see your eyes."
+ button_icon_state = "power_mez"
+ power_explanation = "\
+ Click any player to attempt to mesmerize them, and freeze them in place. \
+ You cannot wear anything covering your face, and both parties must be facing eachother. \
+ If your target is already mesmerized or a Curator, you will fail. \
+ Once mesmerized, the target will be unable to move for a certain amount of time, scaling your rank. \
+ At level 2, your target will additionally be muted. \
+ At level 3, you will be able to use the power through masks and helmets. \
+ At level 5, you will be able to mesmerize regardless of your target's direction."
+ power_flags = NONE
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 30
+ cooldown_time = 20 SECONDS
+ target_range = 8
+ power_activates_immediately = FALSE
+ prefire_message = "Whom will you subvert to your will?"
+ ///Our mesmerized target - Prevents several mesmerizes.
+ var/datum/weakref/target_ref
+
+/datum/action/cooldown/vampire/targeted/mesmerize/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.) // Default checks
+ return FALSE
+ if(!user.getorganslot(ORGAN_SLOT_EYES))
+ // Cant use balloon alert, they've got no eyes!
+ to_chat(user, "You have no eyes with which to mesmerize.")
+ return FALSE
+ // Check: Eyes covered?
+ if(istype(user) && (user.is_eyes_covered() && level_current <= 2) || !isturf(user.loc))
+ user.balloon_alert(user, "your eyes are concealed from sight.")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/mesmerize/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ return isliving(target_atom)
+
+/datum/action/cooldown/vampire/targeted/mesmerize/CheckCanTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/mob/living/current_target = target_atom // We already know it's carbon due to CheckValidTarget()
+ // No mind
+ if(!current_target.mind)
+ owner.balloon_alert(owner, "[current_target] is mindless.")
+ return FALSE
+ // Vampire
+ if(IS_VAMPIRE(current_target))
+ owner.balloon_alert(owner, "vampires are immune to [src].")
+ return FALSE
+ // Dead/Unconscious
+ if(current_target.stat > CONSCIOUS)
+ owner.balloon_alert(owner, "[current_target] is not [(current_target.stat == DEAD || HAS_TRAIT(current_target, TRAIT_FAKEDEATH)) ? "alive" : "conscious"].")
+ return FALSE
+ // Target has eyes?
+ if(!current_target.getorganslot(ORGAN_SLOT_EYES) && !issilicon(current_target))
+ owner.balloon_alert(owner, "[current_target] has no eyes.")
+ return FALSE
+ // Target blind?
+ if(current_target.is_blind() && !issilicon(current_target))
+ owner.balloon_alert(owner, "[current_target] is blind.")
+ return FALSE
+ // Facing target?
+ if(!is_source_facing_target(owner, current_target)) // in unsorted.dm
+ owner.balloon_alert(owner, "you must be facing [current_target].")
+ return FALSE
+ // Target facing me? (On the floor, they're facing everyone)
+ if(((current_target.mobility_flags & MOBILITY_STAND) && !is_source_facing_target(current_target, owner) && level_current <= 4))
+ owner.balloon_alert(owner, "[current_target] must be facing you.")
+ return FALSE
+
+ // Gone through our checks, let's mark our guy.
+ target_ref = WEAKREF(current_target)
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/mesmerize/FireTargetedPower(atom/target_atom)
+ . = ..()
+
+ var/mob/living/user = owner
+ var/mob/living/carbon/mesmerized_target = target_ref.resolve()
+
+ if(issilicon(mesmerized_target))
+ var/mob/living/silicon/mesmerized = mesmerized_target
+ mesmerized.emp_act(EMP_HEAVY)
+ owner.balloon_alert(owner, "temporarily shut [mesmerized] down.")
+ power_activated_sucessfully() // PAY COST! BEGIN COOLDOWN!
+ return
+
+ if(istype(mesmerized_target))
+ owner.balloon_alert(owner, "attempting to hypnotically gaze [mesmerized_target]...")
+
+ if(!do_after(user, 4 SECONDS, mesmerized_target, NONE, TRUE, extra_checks = CALLBACK(src, PROC_REF(ContinueActive), user, mesmerized_target)))
+ return
+
+ var/power_time = 9 SECONDS + level_current * 1.5 SECONDS
+ if(IS_CURATOR(mesmerized_target))
+ to_chat(mesmerized_target, "You feel your eyes burn for a while, but it passes.")
+ return
+ if(HAS_TRAIT_FROM(mesmerized_target, TRAIT_MUTE, TRAIT_VAMPIRE))
+ owner.balloon_alert(owner, "[mesmerized_target] is already in a hypnotic gaze.")
+ return
+ if(iscarbon(mesmerized_target))
+ owner.balloon_alert(owner, "successfully mesmerized [mesmerized_target].")
+ if(level_current >= 2)
+ ADD_TRAIT(mesmerized_target, TRAIT_MUTE, TRAIT_VAMPIRE)
+ mesmerized_target.Immobilize(power_time)
+ mesmerized_target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // mesmerized_target.changeNext_move(power_time) // check click.dm
+ mesmerized_target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze.
+ addtimer(CALLBACK(src, PROC_REF(end_mesmerize), user, mesmerized_target), power_time)
+ power_activated_sucessfully() // PAY COST! BEGIN COOLDOWN!
+
+/datum/action/cooldown/vampire/targeted/mesmerize/DeactivatePower()
+ target_ref = null
+ . = ..()
+
+/datum/action/cooldown/vampire/targeted/mesmerize/proc/end_mesmerize(mob/living/user, mob/living/target)
+ target.notransform = FALSE
+ REMOVE_TRAIT(target, TRAIT_MUTE, TRAIT_VAMPIRE)
+ // They Woke Up! (Notice if within view)
+ if(istype(user) && target.stat == CONSCIOUS && (target in view(6, get_turf(user))))
+ owner.balloon_alert(owner, "[target] snapped out of their trance.")
+
+/datum/action/cooldown/vampire/targeted/mesmerize/ContinueActive(mob/living/user, mob/living/target)
+ return ..() && can_use(user) && CheckCanTarget(target)
diff --git a/code/modules/antagonists/vampire/powers/targeted/trespass.dm b/code/modules/antagonists/vampire/powers/targeted/trespass.dm
new file mode 100644
index 0000000000000..76b69592001cd
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/targeted/trespass.dm
@@ -0,0 +1,103 @@
+/datum/action/cooldown/vampire/targeted/trespass
+ name = "Trespass"
+ desc = "Become mist and advance two tiles in one direction. Useful for skipping past doors and barricades."
+ button_icon_state = "power_tres"
+ power_explanation = "\
+ Click anywhere from 1-2 tiles away from you to teleport. \
+ This power goes through all obstacles except Walls. \
+ Higher levels decrease the sound played from using the Power, and increase the speed of the transition."
+ power_flags = BP_AM_TOGGLE
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = VAMPIRE_CAN_BUY|VASSAL_CAN_BUY
+ bloodcost = 10
+ cooldown_time = 8 SECONDS
+ prefire_message = "Select a destination."
+ //target_range = 2
+ var/turf/target_turf // We need to decide where we're going based on where we clicked. It's not actually the tile we clicked.
+
+/datum/action/cooldown/vampire/targeted/trespass/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(user.notransform || !get_turf(user))
+ return FALSE
+ return TRUE
+
+
+/datum/action/cooldown/vampire/targeted/trespass/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ // Can't target my tile
+ if(target_atom == get_turf(owner) || get_turf(target_atom) == get_turf(owner))
+ return FALSE
+ return TRUE // All we care about is destination. Anything you click is fine.
+
+
+/datum/action/cooldown/vampire/targeted/trespass/CheckCanTarget(atom/target_atom)
+ var/final_turf = isturf(target_atom) ? target_atom : get_turf(target_atom)
+
+ // Are either tiles WALLS?
+ var/turf/from_turf = get_turf(owner)
+ var/this_dir
+ for(var/i = 1 to 2)
+ // Keep Prev Direction if we've reached final turf
+ if(from_turf != final_turf)
+ this_dir = get_dir(from_turf, final_turf) // Recalculate dir so we don't overshoot on a diagonal.
+ from_turf = get_step(from_turf, this_dir)
+ // ERROR! Wall!
+ if(iswallturf(from_turf))
+ var/wallwarning = (i == 1) ? "in the way" : "at your destination"
+ owner.balloon_alert(owner, "There is a wall [wallwarning].")
+ return FALSE
+ // Done
+ target_turf = from_turf
+
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/trespass/FireTargetedPower(atom/target_atom)
+ . = ..()
+
+ // Find target turf, at or below Atom
+ var/mob/living/carbon/user = owner
+ var/turf/my_turf = get_turf(owner)
+
+ user.visible_message(
+ "[user]'s form dissipates into a cloud of mist!",
+ "You disspiate into formless mist.",
+ )
+ // Effect Origin
+ var/sound_strength = max(60, 70 - level_current * 10)
+ playsound(get_turf(owner), 'sound/magic/summon_karp.ogg', sound_strength, 1)
+ var/datum/effect_system/steam_spread/vampire/puff = new /datum/effect_system/steam_spread()
+ puff.set_up(3, 0, my_turf)
+ puff.start()
+
+ var/mist_delay = max(5, 20 - level_current * 2.5) // Level up and do this faster.
+
+ // Freeze Me
+ user.Stun(mist_delay, ignore_canstun = TRUE)
+ user.density = FALSE
+ var/invis_was = user.invisibility
+ user.invisibility = INVISIBILITY_MAXIMUM
+
+ // Wait...
+ sleep(mist_delay / 2)
+ // Move & Freeze
+ if(isturf(target_turf))
+ do_teleport(owner, target_turf, no_effects=TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
+ user.Stun(mist_delay / 2, ignore_canstun = TRUE)
+
+ // Wait...
+ sleep(mist_delay / 2)
+ // Un-Hide & Freeze
+ user.dir = get_dir(my_turf, target_turf)
+ user.Stun(mist_delay / 2, ignore_canstun = TRUE)
+ user.density = 1
+ user.invisibility = invis_was
+ // Effect Destination
+ playsound(get_turf(owner), 'sound/magic/summon_karp.ogg', 60, 1)
+ puff = new /datum/effect_system/steam_spread/()
+ puff.effect_type = /obj/effect/particle_effect/smoke/vampsmoke
+ puff.set_up(3, 0, target_turf)
+ puff.start()
diff --git a/code/modules/antagonists/vampire/powers/tremere/_tremere.dm b/code/modules/antagonists/vampire/powers/tremere/_tremere.dm
new file mode 100644
index 0000000000000..baf56a95eafa7
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/tremere/_tremere.dm
@@ -0,0 +1,26 @@
+/**
+ * # Tremere Powers
+ *
+ * This file is for Tremere power procs and Vampire procs that deals exclusively with Tremere.
+ * Tremere has quite a bit of unique things to it, so I thought it's own subtype would be nice
+ */
+
+/datum/action/cooldown/vampire/targeted/tremere
+ name = "Tremere Gift"
+ desc = "A Tremere exclusive gift."
+ background_icon_state = "tremere_power_off"
+ button_icon_state = "power_auspex"
+
+ background_icon_state_on = "tremere_power_on"
+ background_icon_state_off = "tremere_power_off"
+
+ // Tremere powers don't level up, we have them hardcoded.
+ level_current = 0
+ // Re-defining these as we want total control over them
+ power_flags = BP_AM_TOGGLE|BP_AM_STATIC_COOLDOWN
+ purchase_flags = TREMERE_CAN_BUY
+ // Targeted stuff
+ power_activates_immediately = FALSE
+
+ ///The upgraded version of this Power. 'null' means it's the max level.
+ var/upgraded_power = null
diff --git a/code/modules/antagonists/vampire/powers/tremere/auspex.dm b/code/modules/antagonists/vampire/powers/tremere/auspex.dm
new file mode 100644
index 0000000000000..8f5dd4286a94a
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/tremere/auspex.dm
@@ -0,0 +1,119 @@
+/**
+ * # Auspex
+ *
+ * Level 1 - Cloak of Darkness until clicking an area, teleports the user to the selected area (max 2 tile)
+ * Level 2 - Cloak of Darkness until clicking an area, teleports the user to the selected area (max 3 tiles)
+ * Level 3 - Cloak of Darkness until clicking an area, teleports the user to the selected area
+ * Level 4 - Cloak of Darkness until clicking an area, teleports the user to the selected area, causes nearby people to bleed.
+ * Level 5 - Cloak of Darkness until clicking an area, teleports the user to the selected area, causes nearby people to fall asleep.
+ */
+
+// Look to /datum/action/cooldown/spell/pointed/void_phase for help.
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex
+ name = "Level 1: Auspex"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/auspex/two
+ level_current = 1
+ desc = "Hide yourself within a Cloak of Darkness, click on an area to teleport up to 2 tiles away."
+ button_icon_state = "power_auspex"
+ power_explanation = "\
+ When Activated, you will be hidden in a Cloak of Darkness. \
+ Click any area up to 2 tiles away to teleport there and reveal yourself."
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ bloodcost = 5
+ constant_bloodcost = 2
+ cooldown_time = 12 SECONDS
+ target_range = 2
+ prefire_message = "Where do you wish to teleport to?"
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/two
+ name = "Level 2: Auspex"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/auspex/three
+ level_current = 2
+ desc = "Hide yourself within a Cloak of Darkness, click on an area to teleport up to 3 tiles away."
+ power_explanation = "\
+ When Activated, you will be hidden in a Cloak of Darkness. \
+ Click any area up to 3 tiles away to teleport there and reveal yourself."
+ bloodcost = 10
+ cooldown_time = 10 SECONDS
+ target_range = 3
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/three
+ name = "Level 3: Auspex"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/auspex/advanced
+ level_current = 3
+ desc = "Hide yourself within a Cloak of Darkness, click on an area to teleport."
+ power_explanation = "\
+ When Activated, you will be hidden in a Cloak of Darkness. \
+ Click any area to teleport there and reveal yourself."
+ bloodcost = 15
+ cooldown_time = 8 SECONDS
+ target_range = null
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/advanced
+ name = "Level 4: Auspex"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/auspex/advanced/two
+ level_current = 4
+ desc = "Hide yourself within a Cloak of Darkness, click on an area to teleport, leaving nearby people bleeding."
+ power_explanation = "\
+ When Activated, you will be hidden in a Cloak of Darkness. \
+ Click any area to teleport there and reveal yourself. \
+ Additionally, people at your current location will be left bleeding."
+ background_icon_state_on = "tremere_power_gold_on"
+ background_icon_state_off = "tremere_power_gold_off"
+ bloodcost = 20
+ cooldown_time = 6 SECONDS
+ target_range = null
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/advanced/two
+ name = "Level 5: Auspex"
+ upgraded_power = null
+ level_current = 5
+ desc = "Hide yourself within a Cloak of Darkness, click on an area to teleport, leaving nearby people bleeding and asleep."
+ power_explanation = "\
+ When Activated, you will be hidden in a Cloak of Darkness. \
+ Click any area to teleport there and reveal yourself. \
+ Additionally, people at your targeted location will fall over in pain."
+ bloodcost = 25
+ cooldown_time = 8 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ return isturf(target_atom)
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/ActivatePower(trigger_flags)
+ . = ..()
+ owner.AddElement(/datum/element/digital_camo)
+ animate(owner, alpha = 15, time = 1 SECONDS)
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/DeactivatePower()
+ animate(owner, alpha = 255, time = 1 SECONDS)
+ owner.RemoveElement(/datum/element/digital_camo)
+ return ..()
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/FireTargetedPower(atom/target_atom)
+ . = ..()
+ var/mob/living/user = owner
+ var/turf/targeted_turf = get_turf(target_atom)
+ auspex_blink(user, targeted_turf)
+
+/datum/action/cooldown/vampire/targeted/tremere/auspex/proc/auspex_blink(mob/living/user, turf/targeted_turf)
+ playsound(user, 'sound/magic/summon_karp.ogg', 60)
+ playsound(targeted_turf, 'sound/magic/summon_karp.ogg', 60)
+
+ new /obj/effect/particle_effect/smoke/vampsmoke(user.drop_location())
+ new /obj/effect/particle_effect/smoke/vampsmoke(targeted_turf)
+
+ for(var/mob/living/carbon/living_mob in range(1, targeted_turf)-user)
+ if(IS_VAMPIRE(living_mob) || IS_VASSAL(living_mob))
+ continue
+ if(level_current >= 4)
+ living_mob.add_bleeding(BLEED_CRITICAL)
+ living_mob.adjustBruteLoss(15)
+ if(level_current >= 5)
+ living_mob.Knockdown(10 SECONDS, ignore_canstun = TRUE)
+
+ do_teleport(owner, targeted_turf, no_effects = TRUE, channel = TELEPORT_CHANNEL_QUANTUM)
+ power_activated_sucessfully()
diff --git a/code/modules/antagonists/vampire/powers/tremere/dominate.dm b/code/modules/antagonists/vampire/powers/tremere/dominate.dm
new file mode 100644
index 0000000000000..45b09ca274208
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/tremere/dominate.dm
@@ -0,0 +1,195 @@
+/**
+ * # Dominate;
+ *
+ * Level 1 - Mesmerizes target
+ * Level 2 - Mesmerizes and mutes target
+ * Level 3 - Mesmerizes, blinds and mutes target
+ * Level 4 - Target (if at least in crit & has a mind) will revive as a Mute/Deaf Vassal for 5 minutes before dying.
+ * Level 5 - Target (if at least in crit & has a mind) will revive as a Vassal for 8 minutes before dying.
+ */
+
+// Copied from mesmerize.dm
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate
+ name = "Level 1: Dominate"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/dominate/two
+ level_current = 1
+ desc = "Mesmerize any foe who stands still long enough."
+ button_icon_state = "power_dominate"
+ power_explanation = "\
+ Click any person to mesmerize them after 4 seconds. \
+ This will completely immobilize them for the next 10 seconds."
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_UNCONSCIOUS
+ bloodcost = 15
+ constant_bloodcost = 2
+ cooldown_time = 50 SECONDS
+ target_range = 6
+ prefire_message = "Select a target."
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/two
+ name = "Level 2: Dominate"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/dominate/three
+ level_current = 2
+ desc = "Mesmerize and mute any foe who stands still long enough."
+ power_explanation = "\
+ Click any person to mesmerize them after 4 seconds. \
+ This will completely immobilize and mute them for the next 12 seconds."
+ bloodcost = 20
+ cooldown_time = 40 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/three
+ name = "Level 3: Dominate"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/dominate/advanced
+ level_current = 3
+ desc = "Mesmerize, mute and blind any foe who stands still long enough."
+ power_explanation = "\
+ Click any person to mesmerize them after 4 seconds. \
+ This will completely immobilize, mute, and blind them for the next 14 seconds."
+ bloodcost = 30
+ cooldown_time = 35 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/CheckValidTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ return isliving(target_atom)
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/CheckCanTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/mob/living/selected_target = target_atom
+ if(!selected_target.mind)
+ owner.balloon_alert(owner, "[selected_target] is mindless.")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/advanced
+ name = "Level 4: Possession"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/dominate/advanced/two
+ level_current = 4
+ desc = "Mesmerize, mute and blind any foe who stands still long enough, or convert the damaged to temporary Vassals."
+ power_explanation = "\
+ Click any person to mesmerize them after 4 seconds.\
+ This will completely immobilize, mute, and blind them for the next 14 seconds. \
+ However, if you are adjacent to the target, and they are in critical condition or dead, they will be turned into a temporary mute Vassal. \
+ After 5 minutes, they will die. \
+ If you use this on a dead Vassal, you will revive them."
+ background_icon_state = "tremere_power_gold_off"
+ background_icon_state_on = "tremere_power_gold_on"
+ background_icon_state_off = "tremere_power_gold_off"
+ bloodcost = 80
+ cooldown_time = 3 MINUTES
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/advanced/two
+ name = "Level 5: Possession"
+ desc = "Mesmerize, mute and blind any foe who stands still long enough, or convert the damaged to temporary Vassals."
+ level_current = 5
+ upgraded_power = null
+ power_explanation = "\
+ Click any person to mesmerize them after 4 seconds.\
+ This will completely immobilize, mute, and blind them for the next 14 seconds. \
+ However, if you are adjacent to the target, and they are in critical condition or dead, they will be turned into a temporary mute Vassal. \
+ After 8 minutes, they will die. \
+ If you use this on a dead Vassal, you will revive them."
+ bloodcost = 100
+ cooldown_time = 2 MINUTES
+
+// The advanced version
+/datum/action/cooldown/vampire/targeted/tremere/dominate/advanced/CheckCanTarget(atom/target_atom)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/mob/living/selected_target = target_atom
+ if((IS_VASSAL(selected_target) || selected_target.stat >= SOFT_CRIT) && !owner.Adjacent(selected_target))
+ owner.balloon_alert(owner, "out of range.")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/FireTargetedPower(atom/target_atom)
+ . = ..()
+ var/mob/living/target = target_atom
+ var/mob/living/user = owner
+ if(target.stat >= SOFT_CRIT && user.Adjacent(target) && level_current >= 4)
+ attempt_vassalize(target, user)
+ return
+ else if(IS_VASSAL(target))
+ owner.balloon_alert(owner, "vassal cant be revived")
+ return
+ attempt_mesmerize(target, user)
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/proc/attempt_mesmerize(mob/living/target, mob/living/user)
+ owner.balloon_alert(owner, "attempting to mesmerize.")
+ if(!do_after(user, 3 SECONDS, target, NONE, TRUE))
+ return
+
+ power_activated_sucessfully()
+ var/power_time = 90 + level_current * 15
+ if(IS_CURATOR(target))
+ to_chat(target, "You feel you something crawling under your skin, but it passes.")
+ return
+ if(HAS_TRAIT_FROM(target, TRAIT_MUTE, TRAIT_VAMPIRE))
+ owner.balloon_alert(owner, "[target] is already in some form of hypnotic gaze.")
+ return
+ if(iscarbon(target))
+ var/mob/living/carbon/mesmerized = target
+ owner.balloon_alert(owner, "successfully mesmerized [mesmerized].")
+ if(level_current >= 2)
+ ADD_TRAIT(target, TRAIT_MUTE, TRAIT_VAMPIRE)
+ if(level_current >= 3)
+ target.become_blind(TRAIT_VAMPIRE)
+ mesmerized.Immobilize(power_time)
+ mesmerized.next_move = world.time + power_time
+ mesmerized.notransform = TRUE
+ addtimer(CALLBACK(src, PROC_REF(end_mesmerize), user, target), power_time)
+ if(issilicon(target))
+ var/mob/living/silicon/mesmerized = target
+ mesmerized.emp_act(EMP_HEAVY)
+ owner.balloon_alert(owner, "temporarily shut [mesmerized] down.")
+
+/datum/action/cooldown/vampire/targeted/tremere/proc/end_mesmerize(mob/living/user, mob/living/target)
+ target.notransform = FALSE
+ target.cure_blind(TRAIT_VAMPIRE)
+ REMOVE_TRAIT(target, TRAIT_MUTE, TRAIT_VAMPIRE)
+ if(istype(user) && target.stat == CONSCIOUS && (target in view(6, get_turf(user))))
+ owner.balloon_alert(owner, "[target] snapped out of their trance.")
+
+/datum/action/cooldown/vampire/targeted/tremere/dominate/proc/attempt_vassalize(mob/living/target, mob/living/user)
+ owner.balloon_alert(owner, "attempting to vassalize.")
+ if(!do_after(user, 6 SECONDS, target, NONE, TRUE))
+ return
+
+ if(IS_VASSAL(target))
+ power_activated_sucessfully()
+ to_chat(user, "We revive [target]!")
+ target.mind.grab_ghost()
+ target.revive(full_heal = TRUE)
+ return
+ if(IS_CURATOR(target))
+ to_chat(target, "Their body refuses to react...")
+ return
+ if(!vampiredatum_power.can_make_vassal(target))
+ return
+ vampiredatum_power.make_vassal(target)
+ power_activated_sucessfully()
+ to_chat(user, "We revive [target]!")
+ target.mind.grab_ghost()
+ target.revive(full_heal = TRUE)
+ var/datum/antagonist/vassal/vassaldatum = target.mind.has_antag_datum(/datum/antagonist/vassal)
+ vassaldatum.special_type = TREMERE_VASSAL //don't turn them into a favorite please
+ var/living_time
+ if(level_current == 4)
+ living_time = 5 MINUTES
+ ADD_TRAIT(target, TRAIT_MUTE, TRAIT_VAMPIRE)
+ ADD_TRAIT(owner, TRAIT_DEAF, TRAIT_VAMPIRE)
+ else if(level_current == 5)
+ living_time = 8 MINUTES
+ addtimer(CALLBACK(src, PROC_REF(end_possession), target), living_time)
+
+/datum/action/cooldown/vampire/targeted/tremere/proc/end_possession(mob/living/user)
+ REMOVE_TRAIT(user, TRAIT_MUTE, TRAIT_VAMPIRE)
+ REMOVE_TRAIT(user, TRAIT_DEAF, TRAIT_VAMPIRE)
+ user.mind.remove_antag_datum(/datum/antagonist/vassal)
+ to_chat(user, "You feel the Blood of your Master run out!")
+ user.death()
diff --git a/code/modules/antagonists/vampire/powers/tremere/thaumaturgey.dm b/code/modules/antagonists/vampire/powers/tremere/thaumaturgey.dm
new file mode 100644
index 0000000000000..6b6df79d79124
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/tremere/thaumaturgey.dm
@@ -0,0 +1,184 @@
+/**
+ * # Thaumaturgy
+ *
+ * Level 1 - One shot bloodbeam spell
+ * Level 2 - Bloodbeam spell - Gives them a Blood shield until they use Bloodbeam
+ * Level 3 - Bloodbeam spell that breaks open lockers/doors - Gives them a Blood shield until they use Bloodbeam
+ * Level 4 - Bloodbeam spell that breaks open lockers/doors + double damage to victims - Gives them a Blood shield until they use Bloodbeam
+ * Level 5 - Bloodbeam spell that breaks open lockers/doors + double damage & steals blood - Gives them a Blood shield until they use Bloodbeam
+ */
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy
+ name = "Level 1: Thaumaturgy"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/two
+ desc = "Fire a blood bolt at your enemy, dealing Burn damage."
+ level_current = 1
+ button_icon_state = "power_thaumaturgy"
+ power_explanation = "Shoots a blood bolt spell that deals burn damage"
+ check_flags = BP_CANT_USE_IN_TORPOR|BP_CANT_USE_IN_FRENZY|BP_CANT_USE_WHILE_UNCONSCIOUS
+ bloodcost = 20
+ constant_bloodcost = 0
+ cooldown_time = 6 SECONDS
+ prefire_message = "Click where you wish to fire."
+ ///Blood shield given while this Power is active.
+ var/datum/weakref/blood_shield
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/two
+ name = "Level 2: Thaumaturgy"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/three
+ desc = "Create a Blood shield and fire a blood bolt at your enemy, dealing Burn damage."
+ level_current = 2
+ power_explanation = "\
+ Activating Thaumaturgy will temporarily give you a Blood Shield, \
+ The blood shield has a 75% block chance, but costs 15 Blood per hit to maintain. \
+ You can also fire a blood bolt which will deactivate your shield."
+ prefire_message = "Click where you wish to fire (using your power removes blood shield)."
+ bloodcost = 40
+ cooldown_time = 4 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/three
+ name = "Level 3: Thaumaturgy"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/advanced
+ desc = "Create a Blood shield and fire a blood bolt, dealing Burn damage and opening doors/lockers."
+ level_current = 3
+ power_explanation = "\
+ Activating Thaumaturgy will temporarily give you a Blood Shield, \
+ The blood shield has a 75% block chance, but costs 15 Blood per hit to maintain. \
+ You can also fire a blood bolt which will deactivate your shield. \
+ If the blood bolt hits a locker or door, it will open it."
+ bloodcost = 50
+ cooldown_time = 6 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/advanced
+ name = "Level 4: Blood Strike"
+ upgraded_power = /datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/advanced/two
+ desc = "Create a Blood shield and fire a blood bolt, dealing Burn damage and opening doors/lockers."
+ level_current = 4
+ power_explanation = "\
+ Activating Thaumaturgy will temporarily give you a Blood Shield, \
+ The blood shield has a 75% block chance, but costs 15 Blood per hit to maintain. \
+ You can also fire a blood bolt which will deactivate your shield. \
+ If the blood bolt hits a locker or door, it will open it. \
+ Your blood bolt does more damage."
+ background_icon_state = "tremere_power_gold_off"
+ background_icon_state_on = "tremere_power_gold_on"
+ background_icon_state_off = "tremere_power_gold_off"
+ prefire_message = "Click where you wish to fire (using your power removes blood shield)."
+ bloodcost = 60
+ cooldown_time = 6 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/advanced/two
+ name = "Level 5: Blood Strike"
+ upgraded_power = null
+ desc = "Create a Blood shield and fire a blood bolt, dealing Burn damage, stealing Blood and opening doors/lockers."
+ level_current = 5
+ power_explanation = "\
+ Activating Thaumaturgy will temporarily give you a Blood Shield, \
+ The blood shield has a 75% block chance, but costs 15 Blood per hit to maintain. \
+ You can also fire a blood bolt which will deactivate your shield. \
+ If the blood bolt hits a locker or door, it will open it. \
+ Your blood bolt does more damage, and if it hits a person will steal blood"
+ bloodcost = 80
+ cooldown_time = 8 SECONDS
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/ActivatePower(trigger_flags)
+ . = ..()
+ owner.balloon_alert(owner, "you start thaumaturgy")
+ if(level_current >= 2) // Only if we're at least level 2.
+ var/obj/item/shield/vampire/new_shield = new
+ blood_shield = WEAKREF(new_shield)
+ if(!owner.put_in_inactive_hand(new_shield))
+ owner.balloon_alert(owner, "off hand is full!")
+ to_chat(owner, "Blood shield couldn't be activated as your off hand is full.")
+ return FALSE
+ owner.visible_message(
+ "[owner]\'s hands begins to bleed and forms into a blood shield!",
+ "We activate our Blood shield!",
+ "You hear liquids forming together.",
+ )
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/DeactivatePower()
+ if(blood_shield)
+ QDEL_NULL(blood_shield)
+ return ..()
+
+/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/FireTargetedPower(atom/target_atom)
+ . = ..()
+
+ var/mob/living/user = owner
+ owner.balloon_alert(owner, "you fire a blood bolt!")
+ to_chat(user, "You fire a blood bolt!")
+ user.changeNext_move(CLICK_CD_RANGE)
+ user.newtonian_move(get_dir(target_atom, user))
+ var/obj/projectile/magic/arcane_barrage/vampire/bolt = new(user.loc)
+ bolt.vampire_power = src
+ bolt.firer = user
+ bolt.def_zone = ran_zone(user.get_combat_bodyzone())
+ bolt.preparePixelProjectile(target_atom, user)
+ INVOKE_ASYNC(bolt, TYPE_PROC_REF(/obj/projectile, fire))
+ playsound(user, 'sound/magic/wand_teleport.ogg', 60, TRUE)
+ power_activated_sucessfully()
+
+/**
+ * # Blood Bolt
+ *
+ * This is the projectile this Power will fire.
+ */
+/obj/projectile/magic/arcane_barrage/vampire
+ name = "blood bolt"
+ icon_state = "mini_leaper"
+ damage = 20
+ var/datum/action/cooldown/vampire/targeted/tremere/thaumaturgy/vampire_power
+
+/obj/projectile/magic/arcane_barrage/vampire/on_hit(target)
+ if(istype(target, /obj/structure/closet) && vampire_power.level_current >= 3)
+ var/obj/structure/closet/hit_closet = target
+ if(hit_closet)
+ hit_closet.welded = FALSE
+ hit_closet.locked = FALSE
+ hit_closet.broken = TRUE
+ hit_closet.update_appearance()
+ qdel(src)
+ return BULLET_ACT_HIT
+ if(istype(target, /obj/machinery/door) && vampire_power.level_current >= 3)
+ var/obj/machinery/door/hit_airlock = target
+ hit_airlock.open(2)
+ qdel(src)
+ return BULLET_ACT_HIT
+ if(ismob(target))
+ if(vampire_power.level_current >= 4)
+ damage = 40
+ if(vampire_power.level_current >= 5)
+ var/mob/living/person_hit = target
+ person_hit.blood_volume -= 60
+ vampire_power.vampiredatum_power.AddBloodVolume(60)
+ qdel(src)
+ return BULLET_ACT_HIT
+ . = ..()
+
+/**
+ * # Blood Shield
+ *
+ * The shield spawned when using Thaumaturgy when strong enough.
+ * Copied mostly from '/obj/item/shield/changeling'
+ */
+
+/obj/item/shield/vampire
+ name = "blood shield"
+ desc = "A shield made out of blood, requiring blood to sustain hits."
+ item_flags = ABSTRACT | DROPDEL
+ icon = 'icons/vampires/vamp_obj.dmi'
+ icon_state = "blood_shield"
+ lefthand_file = 'icons/vampires/bs_leftinhand.dmi'
+ righthand_file = 'icons/vampires/bs_rightinhand.dmi'
+ block_power = 75
+
+/obj/item/shield/vampire/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, TRAIT_VAMPIRE)
+
+/obj/item/shield/vampire/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ var/datum/antagonist/vampire/vampiredatum = owner.mind.has_antag_datum(/datum/antagonist/vampire)
+ if(vampiredatum)
+ vampiredatum.AddBloodVolume(-15)
+ return ..()
diff --git a/code/modules/antagonists/vampire/powers/vassal/distress.dm b/code/modules/antagonists/vampire/powers/vassal/distress.dm
new file mode 100644
index 0000000000000..c278de333d440
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/vassal/distress.dm
@@ -0,0 +1,21 @@
+/datum/action/cooldown/vampire/distress
+ name = "Distress"
+ desc = "Injure yourself, allowing you to make a desperate call for help to your Master."
+ button_icon_state = "power_distress"
+ power_explanation = "Use this Power anywhere and your Master will instantly be alerted to your location."
+ power_flags = NONE
+ check_flags = NONE
+ purchase_flags = NONE
+ bloodcost = 10
+ cooldown_time = 10 SECONDS
+
+/datum/action/cooldown/vampire/distress/ActivatePower(trigger_flags)
+ . = ..()
+ var/turf/open/floor/target_area = get_area(owner)
+ var/datum/antagonist/vassal/vassaldatum = IS_VASSAL(owner)
+
+ owner.balloon_alert(owner, "you call out for your master!")
+ to_chat(vassaldatum.master.owner, "[owner], your loyal Vassal, is desperately calling for aid at [target_area]!")
+
+ var/mob/living/user = owner
+ user.adjustBruteLoss(10)
diff --git a/code/modules/antagonists/vampire/powers/vassal/recuperate.dm b/code/modules/antagonists/vampire/powers/vassal/recuperate.dm
new file mode 100644
index 0000000000000..56a0b7288ee70
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/vassal/recuperate.dm
@@ -0,0 +1,63 @@
+/// Used by Vassals
+/datum/action/cooldown/vampire/recuperate
+ name = "Sanguine Recuperation"
+ desc = "Slowly heals you overtime using your master's blood, in exchange for some of your own blood and effort."
+ button_icon_state = "power_recup"
+ power_explanation = "\
+ Activating this Power will begin to heal your wounds. \
+ You will heal Brute and Toxin damage at the cost of your Stamina and blood from both you and your Master. \
+ If you aren't a bloodless race, you will additionally heal Burn damage."
+ power_flags = BP_AM_TOGGLE
+ check_flags = BP_CANT_USE_WHILE_INCAPACITATED|BP_CANT_USE_WHILE_UNCONSCIOUS
+ purchase_flags = NONE
+ bloodcost = 1.5
+ cooldown_time = 10 SECONDS
+
+/datum/action/cooldown/vampire/recuperate/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ if(user.stat >= DEAD || user.incapacitated())
+ user.balloon_alert(user, "you are incapacitated...")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/recuperate/ActivatePower(trigger_flags)
+ . = ..()
+ to_chat(owner, "Your muscles clench as your master's immortal blood mixes with your own, knitting your wounds.")
+ owner.balloon_alert(owner, "recuperate turned on.")
+
+/datum/action/cooldown/vampire/recuperate/UsePower(seconds_per_tick)
+ . = ..()
+ if(!.)
+ return
+
+ if(!active)
+ return
+ var/mob/living/carbon/user = owner
+ var/datum/antagonist/vassal/vassaldatum = IS_VASSAL(user)
+ vassaldatum.master.AddBloodVolume(-1)
+ user.Jitter(5 SECONDS)
+ user.adjustStaminaLoss(bloodcost * 1.1)
+ user.adjustBruteLoss(-2.5)
+ user.adjustToxLoss(-2, forced = TRUE)
+ // Plasmamen won't lose blood, they don't have any, so they don't heal from Burn.
+ if(!HAS_TRAIT(user, TRAIT_NO_BLOOD))
+ user.blood_volume -= bloodcost
+ user.adjustFireLoss(-1.5)
+ // Stop Bleeding
+ if(istype(user) && user.is_bleeding())
+ for(var/obj/item/bodypart/part in user.bodyparts)
+ user.add_bleeding(-10)
+
+/datum/action/cooldown/vampire/recuperate/ContinueActive(mob/living/user, mob/living/target)
+ if(user.stat >= DEAD)
+ return FALSE
+ if(user.incapacitated())
+ owner.balloon_alert(owner, "too exhausted...")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/recuperate/DeactivatePower()
+ owner.balloon_alert(owner, "recuperate turned off.")
+ return ..()
diff --git a/code/modules/antagonists/vampire/powers/vassal/revenge_bloodbag.dm b/code/modules/antagonists/vampire/powers/vassal/revenge_bloodbag.dm
new file mode 100644
index 0000000000000..54919448a22fd
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/vassal/revenge_bloodbag.dm
@@ -0,0 +1,57 @@
+/datum/action/cooldown/vampire/vassal_blood
+ name = "Create Blood"
+ desc = "Convert a blood bag into Vampiric Blood."
+ button_icon_state = "power_bleed"
+ power_explanation = "Use this power with a bloodbag in hand to fill it with Vampiric Blood which is used to reset ex-vassal deconversion timers."
+ power_flags = NONE
+ check_flags = NONE
+ purchase_flags = NONE
+ bloodcost = 150
+ cooldown_time = 10 SECONDS
+
+/datum/action/cooldown/vampire/vassal_blood/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/blood_bag = locate(/obj/item/reagent_containers/blood) in owner?.held_items
+ if(!blood_bag)
+ owner.balloon_alert(owner, "blood bag needed!")
+ return FALSE
+ if(istype(blood_bag, /obj/item/reagent_containers/blood/OMinus/vampire))
+ owner.balloon_alert(owner, "already vampire blood!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/vassal_blood/ActivatePower(trigger_flags)
+ var/blood_bag = locate(/obj/item/reagent_containers/blood) in owner.held_items
+ if(blood_bag)
+ QDEL_NULL(blood_bag)
+ var/obj/item/reagent_containers/blood/OMinus/vampire/new_bag = new(owner.loc)
+ owner.put_in_active_hand(new_bag)
+ DeactivatePower()
+
+/*
+ * Vampire Blood
+ * Slighty darker than normal blood
+ * Artificially made, this must be fed to ex-vassals to keep them on their high.
+ */
+/datum/reagent/blood/vampire
+ color = "#960000"
+
+/datum/reagent/blood/vampire/on_mob_metabolize(mob/living/living)
+ var/datum/antagonist/ex_vassal/former_vassal = IS_EX_VASSAL(living)
+ if(former_vassal)
+ to_chat(living, "You feel the blood restore you... You feel safe.")
+ COOLDOWN_RESET(former_vassal, blood_timer)
+ COOLDOWN_START(former_vassal, blood_timer, 10 MINUTES)
+ return ..()
+
+
+/obj/item/reagent_containers/blood/OMinus/vampire
+ unique_blood = /datum/reagent/blood/vampire
+
+/obj/item/reagent_containers/blood/OMinus/vampire/examine(mob/user)
+ . = ..()
+ if(IS_EX_VASSAL(user) || IS_REVENGE_VASSAL(user))
+ . += "Seems to be just about the same color as your old Master's..."
diff --git a/code/modules/antagonists/vampire/powers/vassal/revenge_checkstatus.dm b/code/modules/antagonists/vampire/powers/vassal/revenge_checkstatus.dm
new file mode 100644
index 0000000000000..c05cbe31b856c
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/vassal/revenge_checkstatus.dm
@@ -0,0 +1,33 @@
+/datum/action/cooldown/vampire/vassal_checkstatus
+ name = "Check Vassals"
+ desc = "Check each ex vassal's status"
+ button_icon_state = "power_mez"
+ power_explanation = "Use this power to check the health and location of all allied vassals"
+ power_flags = NONE
+ check_flags = NONE
+ purchase_flags = NONE
+ bloodcost = 10
+ cooldown_time = 10 SECONDS
+
+/datum/action/cooldown/vampire/vassal_checkstatus/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/datum/antagonist/vassal/revenge/revenge_vassal = IS_REVENGE_VASSAL(owner)
+ if(!revenge_vassal?.ex_vassals.len)
+ owner.balloon_alert(owner, "no vassals!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/vassal_checkstatus/ActivatePower(trigger_flags)
+ var/datum/antagonist/vassal/revenge/revenge_vassal = IS_REVENGE_VASSAL(owner)
+ for(var/datum/antagonist/ex_vassal/former_vassals as anything in revenge_vassal.ex_vassals)
+ var/turf/open/floor/target_area = get_area(owner)
+ var/information = "[former_vassals.owner.current] has [round(COOLDOWN_TIMELEFT(former_vassals, blood_timer) / 600)] minutes left of Blood \
+ [target_area ? "- currently at [target_area]." : "- their location is unknown!"] \
+ [former_vassals.owner.current.stat == DEAD ? "- DEAD." : ""]"
+
+ to_chat(owner, information)
+
+ DeactivatePower()
diff --git a/code/modules/antagonists/vampire/powers/vassal/revenge_fold.dm b/code/modules/antagonists/vampire/powers/vassal/revenge_fold.dm
new file mode 100644
index 0000000000000..0dc1259d8fe68
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/vassal/revenge_fold.dm
@@ -0,0 +1,39 @@
+/datum/action/cooldown/vampire/vassal_fold
+ name = "Reconvert Ex-Vassal"
+ desc = "Bring an Ex-Vassal back into the fold."
+ button_icon_state = "power_torpor"
+ power_explanation = "Use this power while you are grabbing an ex-Vassal to bring them back into the fold."
+ power_flags = NONE
+ check_flags = NONE
+ purchase_flags = NONE
+ bloodcost = 50
+ cooldown_time = 10 SECONDS
+
+/datum/action/cooldown/vampire/vassal_fold/can_use(mob/living/carbon/user, trigger_flags)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/mob/living/target = owner.pulling
+ var/datum/antagonist/ex_vassal/former_vassal = IS_EX_VASSAL(target)
+ if(!isliving(target))
+ return FALSE
+ if(!former_vassal)
+ owner.balloon_alert(owner, "not a former vassal!")
+ return FALSE
+ if(former_vassal?.revenge_vassal)
+ owner.balloon_alert(owner, "already converted!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/vampire/vassal_fold/ActivatePower(trigger_flags)
+ var/mob/living/target = owner.pulling
+ if(!target)
+ return FALSE
+ var/datum/antagonist/ex_vassal/former_vassal = IS_EX_VASSAL(target)
+ if(!former_vassal || former_vassal?.revenge_vassal)
+ return FALSE
+
+ if(do_after(owner, 5 SECONDS, target))
+ former_vassal.return_to_fold(IS_REVENGE_VASSAL(owner))
+ DeactivatePower()
diff --git a/code/modules/antagonists/vampire/powers/veil.dm b/code/modules/antagonists/vampire/powers/veil.dm
new file mode 100644
index 0000000000000..8fe7aeee9bc1b
--- /dev/null
+++ b/code/modules/antagonists/vampire/powers/veil.dm
@@ -0,0 +1,135 @@
+/datum/action/cooldown/vampire/veil
+ name = "Veil of Many Faces"
+ desc = "Disguise yourself in the illusion of another identity."
+ button_icon_state = "power_veil"
+ power_explanation = "\
+ Activating Veil of Many Faces will shroud you in smoke and forge you a new identity.\
+ Your name and appearance will be completely randomized, deactivating the ability off again will restore you to your former self."
+ power_flags = BP_AM_TOGGLE
+ check_flags = BP_CANT_USE_IN_FRENZY
+ purchase_flags = VAMPIRE_DEFAULT_POWER|VASSAL_CAN_BUY
+ bloodcost = 15
+ constant_bloodcost = 0.1
+ cooldown_time = 10 SECONDS
+
+ // Identity Vars
+ var/prev_name
+ var/prev_gender
+ var/prev_skin_tone
+ var/prev_hair_style
+ var/prev_facial_hair_style
+ var/prev_hair_color
+ var/prev_facial_hair_color
+ var/prev_underwear
+ var/prev_undershirt
+ var/prev_socks
+ var/prev_disfigured
+ var/prev_eye_color
+ var/list/prev_features // For lizards and such
+
+/datum/action/cooldown/vampire/veil/ActivatePower(trigger_flags)
+ . = ..()
+ cast_effect() // POOF
+ veil_user()
+ owner.balloon_alert(owner, "veil turned on.")
+
+/datum/action/cooldown/vampire/veil/proc/veil_user()
+ var/mob/living/carbon/human/user = owner
+
+ // Store Prev Appearance
+ prev_gender = user.gender
+ prev_skin_tone = user.skin_tone
+ prev_hair_style = user.hair_style
+ prev_facial_hair_style = user.facial_hair_style
+ prev_hair_color = user.hair_color
+ prev_facial_hair_color = user.facial_hair_color
+ prev_underwear = user.underwear
+ prev_undershirt = user.undershirt
+ prev_socks = user.socks
+ prev_eye_color = user.eye_color
+ prev_disfigured = HAS_TRAIT(user, TRAIT_DISFIGURED) // I was disfigured!
+ prev_features = user.dna.features
+
+ // Change Name
+ prev_name = user.name
+ var/newname = user.dna.species.random_name()
+ user.real_name = newname
+ user.name = newname
+
+ // Change Appearance
+ user.gender = pick(MALE, FEMALE, PLURAL)
+ user.skin_tone = random_skin_tone()
+ user.hair_style = random_hair_style(user.gender)
+ user.facial_hair_style = pick(random_facial_hair_style(user.gender), "Shaved")
+ user.hair_color = random_short_color()
+ user.facial_hair_color = user.hair_color
+ user.underwear = random_underwear(user.gender)
+ user.undershirt = random_undershirt(user.gender)
+ user.socks = random_socks(user.gender)
+ user.eye_color = random_eye_color()
+ if(prev_disfigured)
+ REMOVE_TRAIT(user, TRAIT_DISFIGURED, null)
+ user.dna.features = random_features()
+
+ // Apply Appearance
+ user.SetSpecialVoice(user.name)
+ user.update_body() // Outfit and underwear, also body.
+ user.update_mutant_bodyparts() // Lizard tails etc
+ user.update_hair()
+ user.update_body_parts()
+
+ to_chat(owner, "You mystify the air around your person. Your identity is now altered.")
+
+/datum/action/cooldown/vampire/veil/DeactivatePower()
+ . = ..()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/user = owner
+
+ // Revert Name
+ user.name = prev_name
+ user.real_name = prev_name
+
+ // Revert Appearance
+ user.gender = prev_gender
+ user.skin_tone = prev_skin_tone
+ user.hair_style = prev_hair_style
+ user.facial_hair_style = prev_facial_hair_style
+ user.hair_color = prev_hair_color
+ user.facial_hair_color = prev_facial_hair_color
+ user.underwear = prev_underwear
+ user.undershirt = prev_undershirt
+ user.socks = prev_socks
+ user.eye_color = prev_eye_color
+
+ if(prev_disfigured)
+ //We are ASSUMING husk. // user.status_flags |= DISFIGURED // Restore "Unknown" disfigurement
+ ADD_TRAIT(user, TRAIT_DISFIGURED, TRAIT_HUSK)
+ user.dna.features = prev_features
+
+ // Apply Appearance
+ user.UnsetSpecialVoice()
+ user.update_body() // Outfit and underwear, also body.
+ user.update_hair()
+ user.update_body_parts() // Body itself, maybe skin color?
+
+ cast_effect() // POOF
+ owner.balloon_alert(owner, "veil turned off.")
+
+
+// CAST EFFECT // General effect (poof, splat, etc) when you cast. Doesn't happen automatically!
+/datum/action/cooldown/vampire/veil/proc/cast_effect()
+ // Effect
+ playsound(get_turf(owner), 'sound/magic/smoke.ogg', 20, 1)
+ var/datum/effect_system/steam_spread/vampire/puff = new /datum/effect_system/steam_spread/()
+ puff.set_up(3, 0, get_turf(owner))
+ puff.attach(owner) //OPTIONAL
+ puff.start()
+ owner.spin(8, 1) //Spin around like a loon.
+
+/obj/effect/particle_effect/smoke/vampsmoke
+ opacity = FALSE
+ lifetime = 0
+
+/obj/effect/particle_effect/smoke/vampsmoke/fade_out(frames = 0.8 SECONDS)
+ ..(frames)
diff --git a/code/modules/antagonists/vampire/vassals/datum_vassal.dm b/code/modules/antagonists/vampire/vassals/datum_vassal.dm
new file mode 100644
index 0000000000000..506d33e57c899
--- /dev/null
+++ b/code/modules/antagonists/vampire/vassals/datum_vassal.dm
@@ -0,0 +1,139 @@
+/datum/antagonist/vassal
+ name = "\improper Vassal"
+ roundend_category = "vassals"
+ antagpanel_category = "Vampire"
+ banning_key = ROLE_VAMPIRE
+ show_in_roundend = FALSE
+
+ var/vassal_hud_name = "vassal"
+
+ /// The Master Vampire's antag datum.
+ var/datum/antagonist/vampire/master
+ /// The Vampire's team
+ var/datum/team/vampire/vampire_team
+ /// List of all Purchased Powers, like Vampires.
+ var/list/datum/action/powers = list()
+ ///Whether this vassal is already a special type of Vassal.
+ var/special_type = FALSE
+ ///Description of what this Vassal does.
+ var/vassal_description
+
+/datum/antagonist/vassal/antag_panel_data()
+ return "Master : [master.owner.name]"
+
+/datum/antagonist/vassal/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current_mob = mob_override || owner.current
+
+ current_mob.faction |= FACTION_VAMPIRE
+
+ vampire_team = master.vampire_team
+ vampire_team.add_member(current_mob.mind)
+
+ vampire_team.hud.join_hud(current_mob)
+ set_antag_hud(current_mob, vassal_hud_name)
+
+/datum/antagonist/vassal/remove_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current_mob = mob_override || owner.current
+
+ vampire_team.remove_member(current_mob.mind)
+ vampire_team.hud.leave_hud(current_mob)
+ set_antag_hud(current_mob, null)
+ current_mob.faction -= FACTION_VAMPIRE
+
+/datum/antagonist/vassal/on_gain()
+ RegisterSignal(SSsunlight, COMSIG_SOL_WARNING_GIVEN, PROC_REF(give_warning))
+ /// Enslave them to their Master
+ if(!master || !istype(master, master))
+ return
+ if(special_type)
+ if(!master.special_vassals[special_type])
+ master.special_vassals[special_type] = list()
+ master.special_vassals[special_type] |= src
+ master.vassals |= src
+ owner.enslave_mind_to_creator(master.owner.current)
+ owner.current.log_message("has been vassalized by [master.owner.current]!", LOG_ATTACK, color="#960000")
+ /// Give Recuperate Power
+ BuyPower(new /datum/action/cooldown/vampire/recuperate)
+ /// Give Objectives
+ var/datum/objective/vampire/vassal/vassal_objective = new
+ vassal_objective.owner = owner
+ objectives += vassal_objective
+ /// Give Vampire Language
+ owner.current.grant_all_languages(FALSE, FALSE, TRUE)
+ owner.current.grant_language(/datum/language/vampiric)
+ return ..()
+
+/datum/antagonist/vassal/on_removal()
+ UnregisterSignal(owner.current, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(SSsunlight, COMSIG_SOL_WARNING_GIVEN)
+ //Free them from their Master
+ if(master?.owner)
+ if(special_type && master.special_vassals[special_type])
+ master.special_vassals[special_type] -= src
+ master.vassals -= src
+ owner.enslaved_to = null
+
+ for(var/all_status_traits in owner.current.status_traits)
+ REMOVE_TRAIT(owner.current, all_status_traits, TRAIT_VAMPIRE)
+
+ for(var/datum/action/cooldown/vampire/power as anything in powers)
+ powers -= power
+ power.Remove(owner.current)
+
+ owner.current.remove_language(/datum/language/vampiric)
+ return ..()
+
+/datum/antagonist/vassal/on_body_transfer(mob/living/old_body, mob/living/new_body)
+ . = ..()
+ for(var/datum/action/cooldown/vampire/power as anything in powers)
+ power.Remove(old_body)
+ power.Grant(new_body)
+
+/datum/antagonist/vassal/greet()
+ . = ..()
+ if(silent)
+ return
+
+ to_chat(owner, "You are now the mortal servant of [master.owner.current], a Vampire!")
+ to_chat(owner, "The power of [master.owner.current.p_their()] immortal blood compels you to obey [master.owner.current.p_them()] in all things, even offering your own life to prolong theirs.\n\
+ You are not required to obey any other Vampire, for only [master.owner.current] is your master. The laws of Nanotrasen do not apply to you now; only your vampiric master's word must be obeyed.") // if only there was a /p_theirs() proc...
+ owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)
+ antag_memory += "You are the mortal servant of [master.owner.current], a bloodsucking vampire! "
+ /// Message told to your Master.
+ to_chat(master.owner, "[owner.current] has become addicted to your immortal blood. [owner.current.p_they(TRUE)] [owner.current.p_are()] now your undying servant")
+ master.owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)
+
+/datum/antagonist/vassal/farewell()
+ if(silent)
+ return
+
+ owner.current.visible_message(
+ "[owner.current]'s eyes dart feverishly from side to side, and then stop. [owner.current.p_they(TRUE)] seem[owner.current.p_s()] calm, \
+ like [owner.current.p_they()] [owner.current.p_have()] regained some lost part of [owner.current.p_them()]self.", \
+ "With a snap, you are no longer enslaved to [master.owner]! You breathe in heavily, having regained your free will.")
+ owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)
+ /// Message told to your (former) Master.
+ if(master && master.owner)
+ to_chat(master.owner, "You feel the bond with your vassal [owner.current] has somehow been broken!")
+
+/datum/antagonist/vassal/admin_add(datum/mind/new_owner, mob/admin)
+ var/list/datum/mind/possible_vampires = list()
+ for(var/datum/antagonist/vampire/vampiredatums in GLOB.antagonists)
+ var/datum/mind/vamp = vampiredatums.owner
+ if(!vamp || !vamp?.current || vamp?.current?.stat == DEAD)
+ continue
+ possible_vampires += vamp
+
+ if(!length(possible_vampires))
+ message_admins("[key_name_admin(usr)] tried vassalizing [key_name_admin(new_owner)], but there were no vampires!")
+ return
+ var/datum/mind/choice = input("Which vampire should this vassal belong to?", "Vampire") in possible_vampires
+ if(!choice)
+ return
+ log_admin("[key_name_admin(usr)] turned [key_name_admin(new_owner)] into a vassal of [key_name_admin(choice)]!")
+ var/datum/antagonist/vampire/vampire = choice.has_antag_datum(/datum/antagonist/vampire)
+ master = vampire
+ new_owner.add_antag_datum(src)
+ to_chat(choice, "Through divine intervention, you've gained a new vassal!")
diff --git a/code/modules/antagonists/vampire/vassals/ex_vassal.dm b/code/modules/antagonists/vampire/vassals/ex_vassal.dm
new file mode 100644
index 0000000000000..cc8a5c523b411
--- /dev/null
+++ b/code/modules/antagonists/vampire/vassals/ex_vassal.dm
@@ -0,0 +1,64 @@
+/datum/antagonist/ex_vassal
+ name = "\improper Ex-Vassal"
+ roundend_category = "vassals"
+ antagpanel_category = "Vampire"
+ banning_key = ROLE_VAMPIRE
+ var/vassal_hud_name = "vassal_grey"
+ show_in_roundend = FALSE
+ show_in_antagpanel = FALSE
+ silent = TRUE
+ ui_name = FALSE
+
+ ///The revenge vassal that brought us into the fold.
+ var/datum/antagonist/vassal/revenge/revenge_vassal
+ ///Reuse the vampire team
+ var/datum/team/vampire/vampire_team
+ ///Timer we have to live
+ COOLDOWN_DECLARE(blood_timer)
+
+/datum/antagonist/ex_vassal/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ set_antag_hud(owner.current, vassal_hud_name)
+
+/datum/antagonist/ex_vassal/remove_innate_effects(mob/living/mob_override)
+ . = ..()
+ set_antag_hud(owner.current, null)
+
+/datum/antagonist/ex_vassal/on_removal()
+ if(revenge_vassal)
+ revenge_vassal.ex_vassals -= src
+ revenge_vassal = null
+ blood_timer = null
+
+ vampire_team.remove_member(owner.current.mind)
+ vampire_team.hud.leave_hud(owner.current)
+ set_antag_hud(owner.current, null)
+
+ return ..()
+
+/*
+ * Fold return
+ *
+ * Called when a Revenge vampire gets a vassal back into the fold.
+ */
+/datum/antagonist/ex_vassal/proc/return_to_fold(datum/antagonist/vassal/revenge/mike_ehrmantraut)
+ revenge_vassal = mike_ehrmantraut // what did john fulp willard mean by this
+ revenge_vassal.ex_vassals += src
+
+ vampire_team.add_member(owner.current.mind)
+ vampire_team.hud.join_hud(owner.current)
+ set_antag_hud(owner.current, vassal_hud_name)
+
+ COOLDOWN_START(src, blood_timer, 10 MINUTES)
+ RegisterSignal(src, COMSIG_LIVING_LIFE, PROC_REF(on_life))
+
+/datum/antagonist/ex_vassal/proc/on_life(datum/source, seconds_per_tick, times_fired)
+ SIGNAL_HANDLER
+
+ if(COOLDOWN_TIMELEFT(src, blood_timer) <= 5 MINUTES + 2 && COOLDOWN_TIMELEFT(src, blood_timer) >= 5 MINUTES - 2) //just about halfway
+ to_chat(owner.current, "You need new blood from your Master!")
+ if(!COOLDOWN_FINISHED(src, blood_timer))
+ return
+ to_chat(owner.current, "You are out of blood!")
+ to_chat(revenge_vassal.owner.current, "[owner.current] has ran out of blood and has permanently left the fold!")
+ owner.remove_antag_datum(/datum/antagonist/ex_vassal)
diff --git a/code/modules/antagonists/vampire/vassals/favorite_vassal.dm b/code/modules/antagonists/vampire/vassals/favorite_vassal.dm
new file mode 100644
index 0000000000000..5f84b9de1ea31
--- /dev/null
+++ b/code/modules/antagonists/vampire/vassals/favorite_vassal.dm
@@ -0,0 +1,23 @@
+/**
+ * Favorite Vassal
+ *
+ * Gets some cool abilities depending on the Clan.
+ */
+/datum/antagonist/vassal/favorite
+ name = "\improper Favorite Vassal"
+ show_in_antagpanel = FALSE
+ vassal_hud_name = "vassal6"
+ special_type = FAVORITE_VASSAL
+ vassal_description = "The Favorite Vassal gets unique abilities over other Vassals depending on your Clan \
+ and becomes completely immune to Mindshields. If part of Ventrue, this is the Vassal you will rank up."
+
+ ///Vampire levels, but for Vassals, used by Ventrue.
+ var/vassal_level
+
+/datum/antagonist/vassal/favorite/on_gain()
+ . = ..()
+ SEND_SIGNAL(master, VAMPIRE_MAKE_FAVORITE, src)
+
+///Set the Vassal's rank to their Vampire level
+/datum/antagonist/vassal/favorite/proc/set_vassal_level(mob/living/carbon/human/target)
+ master.vampire_level = vassal_level
diff --git a/code/modules/antagonists/vampire/vassals/misc_procs_vassal.dm b/code/modules/antagonists/vampire/vassals/misc_procs_vassal.dm
new file mode 100644
index 0000000000000..b911ffb6dbe2b
--- /dev/null
+++ b/code/modules/antagonists/vampire/vassals/misc_procs_vassal.dm
@@ -0,0 +1,36 @@
+/datum/antagonist/vassal/proc/give_warning(atom/source, danger_level, vampire_warning_message, vassal_warning_message)
+ SIGNAL_HANDLER
+ if(vassal_warning_message)
+ to_chat(owner, vassal_warning_message)
+
+/// Used when your Master teaches you a new Power.
+/datum/antagonist/vassal/proc/BuyPower(datum/action/cooldown/vampire/power)
+ powers += power
+ power.Grant(owner.current)
+ log_game("[key_name(owner.current)] purchased [power] as a vassal.")
+
+/datum/antagonist/vassal/proc/LevelUpPowers()
+ for(var/datum/action/cooldown/vampire/power in powers)
+ power.level_current++
+
+/// Called when we are made into the Favorite Vassal
+/datum/antagonist/vassal/proc/make_special(datum/antagonist/vassal/vassal_type)
+ //store what we need
+ var/datum/mind/vassal_owner = owner
+ var/datum/antagonist/vampire/vampiredatum = master
+
+ //remove our antag datum
+ silent = TRUE
+ vassal_owner.remove_antag_datum(/datum/antagonist/vassal)
+
+ //give our new one
+ var/datum/antagonist/vassal/vassaldatum = new vassal_type(vassal_owner)
+ vassaldatum.master = vampiredatum
+ vassaldatum.silent = TRUE
+ vassal_owner.add_antag_datum(vassaldatum)
+ vassaldatum.silent = FALSE
+
+ //send alerts of completion
+ to_chat(master, "You have turned [vassal_owner.current] into your [vassaldatum.name]! They will no longer be deconverted upon Mindshielding!")
+ to_chat(vassal_owner, "As Blood drips over your body, you feel closer to your Master... You are now the Favorite Vassal!")
+ vassal_owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 75, FALSE, pressure_affected = FALSE)
diff --git a/code/modules/antagonists/vampire/vassals/revenge_vassal.dm b/code/modules/antagonists/vampire/vassals/revenge_vassal.dm
new file mode 100644
index 0000000000000..96f9b04eb3665
--- /dev/null
+++ b/code/modules/antagonists/vampire/vassals/revenge_vassal.dm
@@ -0,0 +1,89 @@
+/**
+ * Revenge Vassal
+ *
+ * Has the goal to 'get revenge' when their Master dies.
+ */
+/datum/antagonist/vassal/revenge
+ name = "\improper Revenge Vassal"
+ roundend_category = "abandoned Vassals"
+ show_in_roundend = FALSE
+ show_in_antagpanel = FALSE
+ vassal_hud_name = "vassal4"
+ special_type = REVENGE_VASSAL
+ vassal_description = "The Revenge Vassal will not deconvert on your Final Death, \
+ instead they will gain all your Powers, and the objective to take revenge for your demise. \
+ They additionally maintain your Vassals after your departure, rather than become aimless."
+
+ ///all ex-vassals brought back into the fold.
+ var/list/datum/antagonist/ex_vassal/ex_vassals = list()
+
+/datum/antagonist/vassal/revenge/roundend_report()
+ var/list/report = list()
+ report += printplayer(owner)
+ if(objectives.len)
+ report += printobjectives(objectives)
+
+ // Now list their vassals
+ if(ex_vassals.len)
+ report += "The Vassals brought back into the fold were..."
+ for(var/datum/antagonist/ex_vassal/all_vassals as anything in ex_vassals)
+ if(!all_vassals.owner)
+ continue
+ report += "[all_vassals.owner.name] the [all_vassals.owner.assigned_role]"
+
+ return report.Join(" ")
+
+/datum/antagonist/vassal/revenge/on_gain()
+ . = ..()
+ RegisterSignal(master, VAMPIRE_FINAL_DEATH, PROC_REF(on_master_death))
+
+/datum/antagonist/vassal/revenge/on_removal()
+ UnregisterSignal(master, VAMPIRE_FINAL_DEATH)
+ return ..()
+
+/datum/antagonist/vassal/revenge/ui_static_data(mob/user)
+ var/list/data = list()
+ for(var/datum/action/cooldown/vampire/power as anything in powers)
+ var/list/power_data = list()
+
+ power_data["power_name"] = power.name
+ power_data["power_explanation"] = power.power_explanation
+ power_data["power_icon"] = power.button_icon_state
+
+ data["power"] += list(power_data)
+
+ return data + ..()
+
+/datum/antagonist/vassal/revenge/proc/on_master_death(datum/antagonist/vampire/vampiredatum, mob/living/carbon/master)
+ SIGNAL_HANDLER
+
+ show_in_roundend = TRUE
+ for(var/datum/objective/all_objectives as anything in objectives)
+ objectives -= all_objectives
+
+ BuyPower(new /datum/action/cooldown/vampire/vassal_blood)
+ BuyPower(new /datum/action/cooldown/vampire/vassal_checkstatus)
+ BuyPower(new /datum/action/cooldown/vampire/vassal_fold)
+ for(var/datum/action/cooldown/vampire/master_powers as anything in vampiredatum.powers)
+ if(master_powers.purchase_flags & VAMPIRE_DEFAULT_POWER)
+ continue
+ master_powers.Grant(owner.current)
+
+ var/datum/objective/survive/new_objective = new
+ new_objective.name = "Avenge Vampire"
+ new_objective.explanation_text = "Avenge your Vampire's death by recruiting their ex-vassals and continuing their operations."
+ new_objective.owner = owner
+ objectives += new_objective
+
+ if(info_button_ref)
+ QDEL_NULL(info_button_ref)
+
+ ui_name = "AntagInfoRevengeVassal" //give their new ui
+ var/datum/action/antag_info/info_button = new(src)
+ info_button.Grant(owner.current)
+ info_button_ref = WEAKREF(info_button)
+ INVOKE_ASYNC(src, PROC_REF(ui_interact), owner.current)
+
+ // Alert vassal that their master is dead
+ to_chat(owner.current, "Your master has succumbed to final death! Avenge your Vampire's death by recruiting their ex-vassals and continuing their operations.")
+ owner.current.playsound_local(get_turf(owner.current), 'sound/effects/tendril_destroyed.ogg', 30)
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 4b72737714311..59377c78d26b5 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -46,6 +46,10 @@
name = "mysterious old shard"
old_shard = TRUE
+/obj/item/soulstone/vampire
+ theme = THEME_WIZARD
+ required_role = /datum/antagonist/vassal
+
/obj/item/soulstone/pickup(mob/living/user)
..()
if(!role_check(user))
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 4e3fdb50ea5cb..fe289d2f70bca 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -547,6 +547,28 @@
"spiderhunter.png"= 'html/img/spiderhunter.png',
"spiderviper.png"= 'html/img/spiderviper.png',
"spidertarantula.png"= 'html/img/spidertarantula.png',
+ "vampire.caitiff.png"= 'html/img/vampire.caitiff.png',
+ "vampire.malkavian.png"= 'html/img/vampire.malkavian.png',
+ "vampire.nosferatu.png"= 'html/img/vampire.nosferatu.png',
+ "vampire.tremere.png"= 'html/img/vampire.tremere.png',
+ "vampire.power_bleed"= 'html/img/vampire.power_bleed.png',
+ "vampire.power_auspex.png"= 'html/img/vampire.power_auspex.png',
+ "vampire.power_cloak.png"= 'html/img/vampire.power_cloak.png',
+ "vampire.power_distress.png"= 'html/img/vampire.power_distress.png',
+ "vampire.power_dominate.png"= 'html/img/vampire.power_dominate.png',
+ "vampire.power_feed.png"= 'html/img/vampire.power_feed.png',
+ "vampire.power_gohome.png"= 'html/img/vampire.power_gohome.png',
+ "vampire.power_fortitude.png"= 'html/img/vampire.power_fortitude.png',
+ "vampire.power_human.png"= 'html/img/vampire.power_human.png',
+ "vampire.power_lunge.png"= 'html/img/vampire.power_lunge.png',
+ "vampire.power_mez.png"= 'html/img/vampire.power_mez.png',
+ "vampire.power_recup.png"= 'html/img/vampire.power_recup.png',
+ "vampire.power_speed.png"= 'html/img/vampire.power_speed.png',
+ "vampire.power_strength.png"= 'html/img/vampire.power_strength.png',
+ "vampire.power_thaumaturgy.png"= 'html/img/vampire.power_thaumaturgy.png',
+ "vampire.power_torpor.png"= 'html/img/vampire.power_torpor.png',
+ "vampire.power_tres.png"= 'html/img/vampire.power_tres.png',
+ "vampire.power_veil.png"= 'html/img/vampire.power_veil.png',
)
/datum/asset/simple/orbit
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm
index d3c6a80640bb4..0dff4253872b7 100644
--- a/code/modules/language/language_holder.dm
+++ b/code/modules/language/language_holder.dm
@@ -516,6 +516,12 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes())
/datum/language/sylvan = list(LANGUAGE_ATOM)
)
+/datum/language_holder/vampire
+ understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/vampiric = list(LANGUAGE_ATOM))
+ spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/vampiric = list(LANGUAGE_ATOM))
+
/datum/language_holder/empty
understood_languages = null
spoken_languages = null
diff --git a/code/modules/language/vampiric.dm b/code/modules/language/vampiric.dm
new file mode 100644
index 0000000000000..ed2d2cffb7217
--- /dev/null
+++ b/code/modules/language/vampiric.dm
@@ -0,0 +1,21 @@
+/datum/language/vampiric
+ name = "Blah-Sucker"
+ desc = "The native language of the Vampire elders, learned intuitively by Fledglings as they pass from death into immortality."
+ key = "l"
+ space_chance = 40
+ default_priority = 90
+
+ flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD__LINGUIST_ONLY
+ syllables = list(
+ "luk","cha","no","kra","pru","chi","busi","tam","pol","spu","och",
+ "umf","ora","stu","si","ri","li","ka","red","ani","lup","ala","pro",
+ "to","siz","nu","pra","ga","ump","ort","a","ya","yach","tu","lit",
+ "wa","mabo","mati","anta","tat","tana","prol",
+ "tsa","si","tra","te","ele","fa","inz",
+ "nza","est","sti","ra","pral","tsu","ago","esch","chi","kys","praz",
+ "froz","etz","tzil",
+ "t'","k'","t'","k'","th'","tz'"
+ )
+
+ icon_state = "vampire"
+ icon = 'icons/vampires/vampiric.dmi'
diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm
index d157ef3e97069..a64759ecc6cf3 100644
--- a/code/modules/library/lib_codex_gigas.dm
+++ b/code/modules/library/lib_codex_gigas.dm
@@ -10,3 +10,97 @@
author = "Forces beyond your comprehension"
unique = 1
title = "the Codex Gigas"
+
+/obj/item/book/codex_gigas/Initialize(mapload)
+ . = ..()
+ var/turf/current_turf = get_turf(src)
+ new /obj/item/book/kindred(current_turf)
+
+/**
+ * # Archives of the Kindred:
+ *
+ * A book that can only be used by Curators.
+ * When used on a player, after a short timer, will reveal if the player is a Vampire, including their real name and Clan.
+ * This book should not work on Vampires using the Masquerade ability.
+ * If it reveals a Vampire, the Curator will then be able to tell they are a Vampire on examine (Like a Vassal).
+ * Reading it normally will allow Curators to read what each Clan does, with some extra flavor text ones.
+ *
+ * Regular Vampires won't have any negative effects from the book, while everyone else will get burns/eye damage.
+ */
+/obj/item/book/kindred
+ name = "\improper Archive of the Kindred"
+ title = "the Archive of the Kindred"
+ desc = "Cryptic documents explaining hidden truths behind Undead beings. It is said only Curators can decipher what they really mean."
+ icon = 'icons/vampires/vamp_obj.dmi'
+ lefthand_file = 'icons/vampires/bs_leftinhand.dmi'
+ righthand_file = 'icons/vampires/bs_rightinhand.dmi'
+ icon_state = "kindred_book"
+ author = "dozens of generations of Curators"
+ unique = TRUE
+ throw_speed = 1
+ throw_range = 10
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ ///Boolean on whether the book is currently being used, so you can only use it on one person at a time.
+ var/in_use = FALSE
+
+/obj/item/book/kindred/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/stationloving, FALSE, TRUE)
+
+///Attacking someone with the book.
+/obj/item/book/kindred/afterattack(mob/living/target, mob/living/user, flag, params)
+ . = ..()
+ if(!user.can_read(src) || in_use || (target == user) || !ismob(target))
+ return
+ if(!IS_CURATOR(user))
+ if(IS_VAMPIRE(user))
+ to_chat(user, "[src] seems to be too complicated for you. It would be best to leave this for someone else to take.")
+ return
+ to_chat(user, "[src] burns your hands as you try to use it!")
+ user.apply_damage(3, BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ return
+
+ in_use = TRUE
+ user.balloon_alert_to_viewers(user, "reading book...", "looks at [target] and [src]")
+ if(!do_after(user, 3 SECONDS, target, timed_action_flags = NONE, progress = TRUE))
+ to_chat(user, "You quickly close [src].")
+ in_use = FALSE
+ return
+ in_use = FALSE
+
+ var/datum/antagonist/vampire/vampiredatum = IS_VAMPIRE(target)
+ // Are we a Vampire | Are we on Masquerade. If one is true, they will fail.
+ if(IS_VAMPIRE(target) && !HAS_TRAIT(target, TRAIT_MASQUERADE))
+ if(vampiredatum.broke_masquerade)
+ to_chat(user, "[target], also known as '[vampiredatum.return_full_name()]', is indeed a Vampire, but you already knew this.")
+ return
+ to_chat(user, "[target], also known as '[vampiredatum.return_full_name()]', [vampiredatum.my_clan ? "is part of the [vampiredatum.my_clan]!" : "is not part of a clan."] You quickly note this information down, memorizing it.")
+ vampiredatum.break_masquerade()
+ else
+ to_chat(user, "You fail to draw any conclusions to [target] being a Vampire.")
+
+/obj/item/book/kindred/attack_self(mob/living/user)
+ if(!IS_CURATOR(user))
+ if(IS_VAMPIRE(user))
+ to_chat(user, "[src] seems to be too complicated for you. It would be best to leave this for someone else to take.")
+ else
+ to_chat(user, "You feel your eyes unable to read the boring texts...")
+ return
+ ui_interact(user)
+
+/obj/item/book/kindred/ui_interact(mob/living/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "KindredBook", name)
+ ui.open()
+
+/obj/item/book/kindred/ui_static_data(mob/user)
+ var/data = list()
+
+ for(var/datum/vampire_clan/clans as anything in subtypesof(/datum/vampire_clan))
+ var/clan_data = list()
+ clan_data["clan_name"] = initial(clans.name)
+ clan_data["clan_desc"] = initial(clans.description)
+ data["clans"] += list(clan_data)
+
+ return data
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index c2c8abb594648..1abc1ce21b0bf 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -160,6 +160,10 @@
return ..()
/obj/item/clothing/neck/necklace/memento_mori/proc/memento(mob/living/carbon/human/user)
+ if(IS_VAMPIRE(user))
+ to_chat(user, "The Memento notices your undead soul, and refuses to react..")
+ return
+
to_chat(user, "You feel your life being drained by the pendant...")
if(do_after(user, 40, target = user))
to_chat(user, "Your lifeforce is now linked to the pendant! You feel like removing it would kill you, and yet you instinctively know that until then, you won't die.")
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 91d2491cfd301..2087105757a8a 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -260,6 +260,8 @@ bleedsuppress has been replaced for is_bandaged(). Note that is_bleeding() retur
// Takes care blood loss and regeneration
/mob/living/carbon/human/handle_blood()
+ if(mind && IS_VAMPIRE(src)) // vampires should not be affected by blood
+ return FALSE
if((NOBLOOD in dna.species.species_traits) || HAS_TRAIT(src, TRAIT_NO_BLOOD))
cauterise_wounds()
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 95b5d8cab1761..c5a61800400e1 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -99,7 +99,7 @@
readout += " [round(reagent.volume, 0.001)] units of [reagent.name]"
/*
readout += " Stomach:"
- var/obj/item/organ/internal/stomach/belly = getorganslot(ORGAN_SLOT_STOMACH)
+ var/obj/item/organ/stomach/belly = getorganslot(ORGAN_SLOT_STOMACH)
for(var/datum/reagent/bile in belly?.reagents?.reagent_list)
if(!belly.food_reagents[bile.type])
readout += " [round(bile.volume, 0.001)] units of [bile.name]"
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 6cb3b134322a1..320c8a2ff3713 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1300,9 +1300,6 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/carbon/human/species)
/mob/living/carbon/human/species/skeleton
race = /datum/species/skeleton
-/mob/living/carbon/human/species/vampire
- race = /datum/species/vampire
-
/mob/living/carbon/human/species/zombie
race = /datum/species/zombie
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
deleted file mode 100644
index e9e06773bef24..0000000000000
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ /dev/null
@@ -1,205 +0,0 @@
-/datum/species/vampire
- name = "\improper Vampire"
- id = SPECIES_VAMPIRE
- default_color = "FFFFFF"
- species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS)
- inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH,TRAIT_DRINKSBLOOD)
- inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID)
- mutant_bodyparts = list("tail_human" = "None", "ears" = "None", "wings" = "None", "body_size" = "Normal")
- changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | ERT_SPAWN
- exotic_bloodtype = "U"
- use_skintones = TRUE
- mutantheart = /obj/item/organ/heart/vampire
- mutanttongue = /obj/item/organ/tongue/vampire
- examine_limb_id = SPECIES_HUMAN
- skinned_type = /obj/item/stack/sheet/animalhide/human
- 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)
- . = ..()
- to_chat(C, "[info_text]")
- C.skin_tone = "albino"
- C.update_body(0)
- if(isnull(batform))
- batform = new
- C.AddSpell(batform)
-
-/datum/species/vampire/on_species_loss(mob/living/carbon/C)
- . = ..()
- if(!isnull(batform))
- C.RemoveSpell(batform)
- QDEL_NULL(batform)
-
-/datum/species/vampire/spec_life(mob/living/carbon/human/C)
- . = ..()
- if(istype(C.loc, /obj/structure/closet/crate/coffin))
- C.heal_overall_damage(4,4,0, BODYTYPE_ORGANIC)
- C.adjustToxLoss(-4)
- C.adjustOxyLoss(-4)
- C.adjustCloneLoss(-4)
- return
- C.blood_volume -= 0.75
- if(C.blood_volume <= BLOOD_VOLUME_SURVIVE)
- to_chat(C, "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.investigate_log("has been dusted by a lack of blood (vampire).", INVESTIGATE_DEATHS)
- C.dust()
- var/area/A = get_area(C)
- if(istype(A, /area/chapel))
- to_chat(C, "You don't belong here!")
- C.adjustFireLoss(20)
- C.adjust_fire_stacks(6)
- C.IgniteMob()
-
-/datum/species/vampire/check_species_weakness(obj/item/weapon, mob/living/attacker)
- if(istype(weapon, /obj/item/nullrod/whip))
- return 1 //Whips deal 2x damage to vampires. Vampire killer.
- return 0
-
-/datum/species/vampire/get_species_description()
- return "A classy Vampire! They descend upon Space Station Thirteen Every year to spook the crew! \"Bleeg!!\""
-
-/datum/species/vampire/get_species_lore()
- return list(
- "Vampires are unholy beings blessed and cursed with The Thirst. \
- The Thirst requires them to feast on blood to stay alive, and in return it gives them many bonuses."
- )
-
-/datum/species/vampire/create_pref_unique_perks()
- var/list/to_add = list()
-
- to_add += list(
- list(
- SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
- SPECIES_PERK_ICON = "bed",
- SPECIES_PERK_NAME = "Coffin Brooding",
- SPECIES_PERK_DESC = "Vampires can delay The Thirst and heal by resting in a coffin. So THAT'S why they do that!",
- ),
- list(
- SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
- SPECIES_PERK_ICON = "cross",
- SPECIES_PERK_NAME = "Against God and Nature",
- SPECIES_PERK_DESC = "Almost all higher powers are disgusted by the existence of \
- Vampires, and entering the Chapel is essentially suicide. Do not do it!",
- ),
- )
-
- return to_add
-
-// Vampire blood is special, so it needs to be handled with its own entry.
-/datum/species/vampire/create_pref_blood_perks()
- var/list/to_add = list()
-
- to_add += list(list(
- SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
- SPECIES_PERK_ICON = "tint",
- SPECIES_PERK_NAME = "The Thirst",
- SPECIES_PERK_DESC = "In place of eating, Vampires suffer from The Thirst. \
- Thirst of what? Blood! Their tongue allows them to grab people and drink \
- their blood, and they will die if they run out. As a note, it doesn't \
- matter whose blood you drink, it will all be converted into your blood \
- type when consumed.",
- ))
-
- return to_add
-
-// There isn't a "Minor Undead" biotype, so we have to explain it in an override (see: dullahans)
-/datum/species/vampire/create_pref_biotypes_perks()
- var/list/to_add = list()
-
- to_add += list(list(
- SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
- SPECIES_PERK_ICON = "skull",
- SPECIES_PERK_NAME = "Minor Undead",
- SPECIES_PERK_DESC = "[name] are minor undead. \
- Minor undead enjoy some of the perks of being dead, like \
- not needing to breathe or eat, but do not get many of the \
- environmental immunities involved with being fully undead.",
- ))
-
- return to_add
-
-/obj/item/organ/tongue/vampire
- name = "vampire tongue"
- actions_types = list(/datum/action/item_action/organ_action/vampire)
- color = "#1C1C1C"
- var/drain_cooldown = 0
-
-#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."
-
-/datum/action/item_action/organ_action/vampire/Trigger()
- . = ..()
- if(iscarbon(owner))
- var/mob/living/carbon/H = owner
- var/obj/item/organ/tongue/vampire/V = target
- if(V.drain_cooldown >= world.time)
- to_chat(H, "You just drained blood, wait a few seconds.")
- return
- if(H.pulling && iscarbon(H.pulling))
- var/mob/living/carbon/victim = H.pulling
- if(H.blood_volume >= BLOOD_VOLUME_MAXIMUM)
- to_chat(H, "You're already full!")
- return
- if(victim.stat == DEAD)
- to_chat(H, "You need a living victim!")
- return
- if(!victim.blood_volume || (victim.dna && ((NOBLOOD in victim.dna.species.species_traits) || victim.dna.species.exotic_blood)))
- to_chat(H, "[victim] doesn't have blood!")
- return
- V.drain_cooldown = world.time + 30
- if(victim.anti_magic_check(FALSE, TRUE, FALSE))
- to_chat(victim, "[H] tries to bite you, but stops before touching you!")
- to_chat(H, "[victim] is blessed! You stop just in time to avoid catching fire.")
- return
- if(victim?.reagents?.has_reagent(/datum/reagent/consumable/garlic))
- to_chat(victim, "[H] tries to bite you, but recoils in disgust!")
- to_chat(H, "[victim] reeks of garlic! you can't bring yourself to drain such tainted blood.")
- return
- if(!do_after(H, 3 SECONDS, target = victim, hidden = TRUE))
- return
- var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - H.blood_volume //How much capacity we have left to absorb blood
- var/drained_blood = min(victim.blood_volume, VAMP_DRAIN_AMOUNT, blood_volume_difference)
- to_chat(victim, "[H] is draining your blood!")
- to_chat(H, "You drain some blood!")
- playsound(H, 'sound/items/drink.ogg', 30, 1, -2)
- victim.blood_volume = clamp(victim.blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
- H.blood_volume = clamp(H.blood_volume + drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
- if(!victim.blood_volume)
- to_chat(H, "You finish off [victim]'s blood supply!")
-
-#undef VAMP_DRAIN_AMOUNT
-
-/obj/item/organ/heart/vampire
- name = "vampire heart"
- actions_types = list(/datum/action/item_action/organ_action/vampire_heart)
- color = "#1C1C1C"
-
-/datum/action/item_action/organ_action/vampire_heart
- name = "Check Blood Level"
- desc = "Check how much blood you have remaining."
-
-/datum/action/item_action/organ_action/vampire_heart/Trigger()
- . = ..()
- if(iscarbon(owner))
- var/mob/living/carbon/H = owner
- to_chat(H, "Current blood level: [H.blood_volume]/[BLOOD_VOLUME_MAXIMUM].")
-
-/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/vampire
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index 89655acc213bf..6b9c02602af17 100755
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -429,7 +429,7 @@
metabolization_rate = 0.15 * REAGENTS_METABOLISM
/datum/reagent/consumable/garlic/on_mob_life(mob/living/carbon/M)
- if(isvampire(M)) //incapacitating but not lethal. Unfortunately, vampires cannot vomit.
+ if(IS_VAMPIRE(M)) //incapacitating but not lethal. Unfortunately, vampires cannot vomit.
if(prob(min(25,current_cycle)))
to_chat(M, "You can't get the scent of garlic out of your nose! You can barely think...")
M.Paralyze(10)
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index 37cb861f31e3b..1b9f0552c979e 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -87,21 +87,34 @@
/obj/item/reagent_containers/blood/universal
blood_type = "U"
-/obj/item/reagent_containers/blood/attackby(obj/item/I, mob/user, params)
- if (istype(I, /obj/item/pen) || istype(I, /obj/item/toy/crayon))
- if(!user.is_literate())
- to_chat(user, "You scribble illegibly on the label of [src]!")
- return
- var/t = stripped_input(user, "What would you like to label the blood pack?", name, null, 53)
- if(!user.canUseTopic(src, BE_CLOSE))
- return
- if(user.get_active_held_item() != I)
+/obj/item/reagent_containers/blood/attack(mob/living/victim, mob/living/attacker, params)
+ if(!can_drink(victim, attacker))
+ return
+
+ if(victim != attacker)
+ if(!do_after(victim, 5 SECONDS, attacker))
return
- if(t)
- labelled = 1
- name = "blood pack - [t]"
- else
- labelled = 0
- update_pack_name()
- else
- return ..()
+ attacker.visible_message(
+ "[attacker] forces [victim] to drink from the [src].",
+ "You put the [src] up to [victim]'s mouth.",
+ )
+ reagents.trans_to(victim, 5, transfered_by = attacker, method = INGEST)
+ playsound(victim.loc, 'sound/items/drink.ogg', 30, 1)
+ return TRUE
+
+ while(do_after(victim, 1 SECONDS, timed_action_flags = IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(can_drink), victim, attacker)))
+ victim.visible_message(
+ "[victim] puts the [src] up to their mouth.",
+ "You take a sip from the [src].",
+ )
+ reagents.trans_to(victim, 5, transfered_by = attacker, method = INGEST)
+ playsound(victim.loc, 'sound/items/drink.ogg', 30, 1)
+ return TRUE
+
+/obj/item/reagent_containers/blood/proc/can_drink(mob/living/victim, mob/living/attacker)
+ if(!canconsume(victim, attacker))
+ return FALSE
+ if(!reagents || !reagents.total_volume)
+ to_chat(victim, "[src] is empty!")
+ return FALSE
+ return TRUE
diff --git a/config/game_options.txt b/config/game_options.txt
index d6640f5e05b08..64b39cb0b217b 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -519,7 +519,6 @@ ROUNDSTART_RACES plasmaman
#ROUNDSTART_RACES shadow
#ROUNDSTART_RACES dullahan
#ROUNDSTART_RACES pumpkin_man
-#ROUNDSTART_RACES vampire
## OP Halloween races:
#ROUNDSTART_RACES skeleton
diff --git a/html/img/vampire.caitiff.png b/html/img/vampire.caitiff.png
new file mode 100644
index 0000000000000..0fb3cc1240e52
Binary files /dev/null and b/html/img/vampire.caitiff.png differ
diff --git a/html/img/vampire.malkavian.png b/html/img/vampire.malkavian.png
new file mode 100644
index 0000000000000..b9c8adcaef348
Binary files /dev/null and b/html/img/vampire.malkavian.png differ
diff --git a/html/img/vampire.nosferatu.png b/html/img/vampire.nosferatu.png
new file mode 100644
index 0000000000000..f43d3798f6cd3
Binary files /dev/null and b/html/img/vampire.nosferatu.png differ
diff --git a/html/img/vampire.power_auspex.png b/html/img/vampire.power_auspex.png
new file mode 100644
index 0000000000000..4c25d628151aa
Binary files /dev/null and b/html/img/vampire.power_auspex.png differ
diff --git a/html/img/vampire.power_bleed.png b/html/img/vampire.power_bleed.png
new file mode 100644
index 0000000000000..92c8a08823668
Binary files /dev/null and b/html/img/vampire.power_bleed.png differ
diff --git a/html/img/vampire.power_cloak.png b/html/img/vampire.power_cloak.png
new file mode 100644
index 0000000000000..e6ff781d11c27
Binary files /dev/null and b/html/img/vampire.power_cloak.png differ
diff --git a/html/img/vampire.power_distress.png b/html/img/vampire.power_distress.png
new file mode 100644
index 0000000000000..3babc09f3c586
Binary files /dev/null and b/html/img/vampire.power_distress.png differ
diff --git a/html/img/vampire.power_dominate.png b/html/img/vampire.power_dominate.png
new file mode 100644
index 0000000000000..a82a836b4617e
Binary files /dev/null and b/html/img/vampire.power_dominate.png differ
diff --git a/html/img/vampire.power_feed.png b/html/img/vampire.power_feed.png
new file mode 100644
index 0000000000000..4eb79d6078c07
Binary files /dev/null and b/html/img/vampire.power_feed.png differ
diff --git a/html/img/vampire.power_fortitude.png b/html/img/vampire.power_fortitude.png
new file mode 100644
index 0000000000000..a8433aef88cbb
Binary files /dev/null and b/html/img/vampire.power_fortitude.png differ
diff --git a/html/img/vampire.power_gohome.png b/html/img/vampire.power_gohome.png
new file mode 100644
index 0000000000000..8f25556d15768
Binary files /dev/null and b/html/img/vampire.power_gohome.png differ
diff --git a/html/img/vampire.power_human.png b/html/img/vampire.power_human.png
new file mode 100644
index 0000000000000..6eef4c5308229
Binary files /dev/null and b/html/img/vampire.power_human.png differ
diff --git a/html/img/vampire.power_lunge.png b/html/img/vampire.power_lunge.png
new file mode 100644
index 0000000000000..981ab3937582d
Binary files /dev/null and b/html/img/vampire.power_lunge.png differ
diff --git a/html/img/vampire.power_mez.png b/html/img/vampire.power_mez.png
new file mode 100644
index 0000000000000..46ecf67d6d3ad
Binary files /dev/null and b/html/img/vampire.power_mez.png differ
diff --git a/html/img/vampire.power_recup.png b/html/img/vampire.power_recup.png
new file mode 100644
index 0000000000000..2732354225710
Binary files /dev/null and b/html/img/vampire.power_recup.png differ
diff --git a/html/img/vampire.power_speed.png b/html/img/vampire.power_speed.png
new file mode 100644
index 0000000000000..e7d1e87a196d6
Binary files /dev/null and b/html/img/vampire.power_speed.png differ
diff --git a/html/img/vampire.power_strength.png b/html/img/vampire.power_strength.png
new file mode 100644
index 0000000000000..019abacc62bb1
Binary files /dev/null and b/html/img/vampire.power_strength.png differ
diff --git a/html/img/vampire.power_thaumaturgy.png b/html/img/vampire.power_thaumaturgy.png
new file mode 100644
index 0000000000000..9db64190f9f06
Binary files /dev/null and b/html/img/vampire.power_thaumaturgy.png differ
diff --git a/html/img/vampire.power_torpor.png b/html/img/vampire.power_torpor.png
new file mode 100644
index 0000000000000..14c74329e4291
Binary files /dev/null and b/html/img/vampire.power_torpor.png differ
diff --git a/html/img/vampire.power_tres.png b/html/img/vampire.power_tres.png
new file mode 100644
index 0000000000000..bb3c70b2b3411
Binary files /dev/null and b/html/img/vampire.power_tres.png differ
diff --git a/html/img/vampire.power_veil.png b/html/img/vampire.power_veil.png
new file mode 100644
index 0000000000000..4d38520f8730f
Binary files /dev/null and b/html/img/vampire.power_veil.png differ
diff --git a/html/img/vampire.tremere.png b/html/img/vampire.tremere.png
new file mode 100644
index 0000000000000..a22c115bdf806
Binary files /dev/null and b/html/img/vampire.tremere.png differ
diff --git a/html/img/vampire.ventrue.png b/html/img/vampire.ventrue.png
new file mode 100644
index 0000000000000..c7abf49b2be16
Binary files /dev/null and b/html/img/vampire.ventrue.png differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 27b73165e759f..3625796d0dfd5 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/hud/radials/radial_generic.dmi b/icons/hud/radials/radial_generic.dmi
index 9406148c1def9..d89ea135388f2 100644
Binary files a/icons/hud/radials/radial_generic.dmi and b/icons/hud/radials/radial_generic.dmi differ
diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi
index 13d032eeee32e..57b8e83441c5b 100644
Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ
diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi
index cda07d5a8a15a..65ed70938793d 100644
Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ
diff --git a/icons/vampires/actions_vampire.dmi b/icons/vampires/actions_vampire.dmi
new file mode 100644
index 0000000000000..a2c60fa1189a4
Binary files /dev/null and b/icons/vampires/actions_vampire.dmi differ
diff --git a/icons/vampires/bs_leftinhand.dmi b/icons/vampires/bs_leftinhand.dmi
new file mode 100644
index 0000000000000..da5a9376ba9a8
Binary files /dev/null and b/icons/vampires/bs_leftinhand.dmi differ
diff --git a/icons/vampires/bs_rightinhand.dmi b/icons/vampires/bs_rightinhand.dmi
new file mode 100644
index 0000000000000..efd8a40a9d2c6
Binary files /dev/null and b/icons/vampires/bs_rightinhand.dmi differ
diff --git a/icons/vampires/clan_icons.dmi b/icons/vampires/clan_icons.dmi
new file mode 100644
index 0000000000000..f6242a2143db5
Binary files /dev/null and b/icons/vampires/clan_icons.dmi differ
diff --git a/icons/vampires/stakes.dmi b/icons/vampires/stakes.dmi
new file mode 100644
index 0000000000000..dfc1dc08bf433
Binary files /dev/null and b/icons/vampires/stakes.dmi differ
diff --git a/icons/vampires/vamp_obj.dmi b/icons/vampires/vamp_obj.dmi
new file mode 100644
index 0000000000000..a6386883b35ce
Binary files /dev/null and b/icons/vampires/vamp_obj.dmi differ
diff --git a/icons/vampires/vamp_obj_64.dmi b/icons/vampires/vamp_obj_64.dmi
new file mode 100644
index 0000000000000..4367da28b32b8
Binary files /dev/null and b/icons/vampires/vamp_obj_64.dmi differ
diff --git a/icons/vampires/vampiric.dmi b/icons/vampires/vampiric.dmi
new file mode 100644
index 0000000000000..258cc7a542f9b
Binary files /dev/null and b/icons/vampires/vampiric.dmi differ
diff --git a/sound/misc/ghosty_wind.ogg b/sound/misc/ghosty_wind.ogg
new file mode 100644
index 0000000000000..39bdb52a7d28a
Binary files /dev/null and b/sound/misc/ghosty_wind.ogg differ
diff --git a/sound/vampires/VampireAlert.ogg b/sound/vampires/VampireAlert.ogg
new file mode 100644
index 0000000000000..100e81018c388
Binary files /dev/null and b/sound/vampires/VampireAlert.ogg differ
diff --git a/sound/vampires/blood_vial_slurp.ogg b/sound/vampires/blood_vial_slurp.ogg
new file mode 100644
index 0000000000000..89504b3f5d71d
Binary files /dev/null and b/sound/vampires/blood_vial_slurp.ogg differ
diff --git a/sound/vampires/coffin_close.ogg b/sound/vampires/coffin_close.ogg
new file mode 100644
index 0000000000000..9f5852d65b814
Binary files /dev/null and b/sound/vampires/coffin_close.ogg differ
diff --git a/sound/vampires/coffin_open.ogg b/sound/vampires/coffin_open.ogg
new file mode 100644
index 0000000000000..d936db143e77e
Binary files /dev/null and b/sound/vampires/coffin_open.ogg differ
diff --git a/sound/vampires/griffin_1.ogg b/sound/vampires/griffin_1.ogg
new file mode 100644
index 0000000000000..d722f609e70c7
Binary files /dev/null and b/sound/vampires/griffin_1.ogg differ
diff --git a/sound/vampires/griffin_10.ogg b/sound/vampires/griffin_10.ogg
new file mode 100644
index 0000000000000..b1c1138d70e6e
Binary files /dev/null and b/sound/vampires/griffin_10.ogg differ
diff --git a/sound/vampires/griffin_2.ogg b/sound/vampires/griffin_2.ogg
new file mode 100644
index 0000000000000..4b122afcde4ab
Binary files /dev/null and b/sound/vampires/griffin_2.ogg differ
diff --git a/sound/vampires/griffin_3.ogg b/sound/vampires/griffin_3.ogg
new file mode 100644
index 0000000000000..7d73ad576531d
Binary files /dev/null and b/sound/vampires/griffin_3.ogg differ
diff --git a/sound/vampires/griffin_4.ogg b/sound/vampires/griffin_4.ogg
new file mode 100644
index 0000000000000..38835540a7218
Binary files /dev/null and b/sound/vampires/griffin_4.ogg differ
diff --git a/sound/vampires/griffin_5.ogg b/sound/vampires/griffin_5.ogg
new file mode 100644
index 0000000000000..878fd6f40ecb6
Binary files /dev/null and b/sound/vampires/griffin_5.ogg differ
diff --git a/sound/vampires/griffin_6.ogg b/sound/vampires/griffin_6.ogg
new file mode 100644
index 0000000000000..4f7e0eb2c6374
Binary files /dev/null and b/sound/vampires/griffin_6.ogg differ
diff --git a/sound/vampires/griffin_7.ogg b/sound/vampires/griffin_7.ogg
new file mode 100644
index 0000000000000..f3b76da177169
Binary files /dev/null and b/sound/vampires/griffin_7.ogg differ
diff --git a/sound/vampires/griffin_8.ogg b/sound/vampires/griffin_8.ogg
new file mode 100644
index 0000000000000..8c328fd723249
Binary files /dev/null and b/sound/vampires/griffin_8.ogg differ
diff --git a/sound/vampires/griffin_9.ogg b/sound/vampires/griffin_9.ogg
new file mode 100644
index 0000000000000..f7d6fcbdd254e
Binary files /dev/null and b/sound/vampires/griffin_9.ogg differ
diff --git a/sound/vampires/jackinthebomb.ogg b/sound/vampires/jackinthebomb.ogg
new file mode 100644
index 0000000000000..a49fa02eac15e
Binary files /dev/null and b/sound/vampires/jackinthebomb.ogg differ
diff --git a/sound/vampires/lunge_warn.ogg b/sound/vampires/lunge_warn.ogg
new file mode 100644
index 0000000000000..db49b1e56ce2f
Binary files /dev/null and b/sound/vampires/lunge_warn.ogg differ
diff --git a/sound/vampires/monsterhunterintro.ogg b/sound/vampires/monsterhunterintro.ogg
new file mode 100644
index 0000000000000..d3a0367a08192
Binary files /dev/null and b/sound/vampires/monsterhunterintro.ogg differ
diff --git a/sound/vampires/moonlightbeam.ogg b/sound/vampires/moonlightbeam.ogg
new file mode 100644
index 0000000000000..d54302a611565
Binary files /dev/null and b/sound/vampires/moonlightbeam.ogg differ
diff --git a/sound/vampires/moonlightsword.ogg b/sound/vampires/moonlightsword.ogg
new file mode 100644
index 0000000000000..95df7ba5d71cf
Binary files /dev/null and b/sound/vampires/moonlightsword.ogg differ
diff --git a/sound/vampires/owl_1.ogg b/sound/vampires/owl_1.ogg
new file mode 100644
index 0000000000000..ed25c6ac4148c
Binary files /dev/null and b/sound/vampires/owl_1.ogg differ
diff --git a/sound/vampires/owl_10.oga b/sound/vampires/owl_10.oga
new file mode 100644
index 0000000000000..97f45d4337886
Binary files /dev/null and b/sound/vampires/owl_10.oga differ
diff --git a/sound/vampires/owl_2.ogg b/sound/vampires/owl_2.ogg
new file mode 100644
index 0000000000000..12f26fb6223ea
Binary files /dev/null and b/sound/vampires/owl_2.ogg differ
diff --git a/sound/vampires/owl_3.ogg b/sound/vampires/owl_3.ogg
new file mode 100644
index 0000000000000..f64b193e4ee3e
Binary files /dev/null and b/sound/vampires/owl_3.ogg differ
diff --git a/sound/vampires/owl_5.ogg b/sound/vampires/owl_5.ogg
new file mode 100644
index 0000000000000..e4fd7cd2bb083
Binary files /dev/null and b/sound/vampires/owl_5.ogg differ
diff --git a/sound/vampires/owl_6.ogg b/sound/vampires/owl_6.ogg
new file mode 100644
index 0000000000000..8dacf98503728
Binary files /dev/null and b/sound/vampires/owl_6.ogg differ
diff --git a/sound/vampires/owl_7.ogg b/sound/vampires/owl_7.ogg
new file mode 100644
index 0000000000000..249f171052cf7
Binary files /dev/null and b/sound/vampires/owl_7.ogg differ
diff --git a/sound/vampires/owl_8.ogg b/sound/vampires/owl_8.ogg
new file mode 100644
index 0000000000000..0439517a30f17
Binary files /dev/null and b/sound/vampires/owl_8.ogg differ
diff --git a/sound/vampires/owl_9.ogg b/sound/vampires/owl_9.ogg
new file mode 100644
index 0000000000000..54bc5f971ff11
Binary files /dev/null and b/sound/vampires/owl_9.ogg differ
diff --git a/sound/vampires/paradoxskip.ogg b/sound/vampires/paradoxskip.ogg
new file mode 100644
index 0000000000000..3742a630717e6
Binary files /dev/null and b/sound/vampires/paradoxskip.ogg differ
diff --git a/sound/vampires/rabbitlocator.ogg b/sound/vampires/rabbitlocator.ogg
new file mode 100644
index 0000000000000..027d9201e44a4
Binary files /dev/null and b/sound/vampires/rabbitlocator.ogg differ
diff --git a/sound/vampires/weaponsmithing.ogg b/sound/vampires/weaponsmithing.ogg
new file mode 100644
index 0000000000000..2e54f919dda56
Binary files /dev/null and b/sound/vampires/weaponsmithing.ogg differ
diff --git a/sound/vampires/wonderlandmusic.ogg b/sound/vampires/wonderlandmusic.ogg
new file mode 100644
index 0000000000000..12d4e80f126ff
Binary files /dev/null and b/sound/vampires/wonderlandmusic.ogg differ
diff --git a/strings/malkavian_revelations.json b/strings/malkavian_revelations.json
new file mode 100644
index 0000000000000..3fedeba06a2c9
--- /dev/null
+++ b/strings/malkavian_revelations.json
@@ -0,0 +1,117 @@
+{
+ "revelations": [
+ "#There could have an entirely separate dimension only visible through pools of Blood, and we will never see it.",
+ "#Explosions happen often, I wonder if one will strike me one day. I wonder if I'll survive.",
+ "#The Captain will fall eventually, everything is only a matter of time.",
+ "#There's always something there to be enlightened from. Something to learn. Something to teach.",
+ "#Why have we been abandoned in this universe... When will we be taken away to the rest of the living?",
+ "#When will the dreams stop following me? Why have they picked me, of all people?",
+ "#The one listening to the voices in his head is called foolish from those unaware, but does that make him insane?",
+ "#Oh dear... perhaps I've taken my life a little too far today. I wonder what's next.",
+ "#Sometimes I feel like I am the last prophet to ever exist... Maybe I am.",
+ "#I wonder what made me this way. Is it my Malkavian blood? My hatred for those around me?",
+ "#Maybe I should start to think about what consequences my actions lead me to. Or maybe it's best not to think about it.",
+ "#If I focus on my goals, rather than what I want to do, am I truly happy?",
+ "#Maybe... in an alternate universe... I could be part of another family. One that cares more about me...",
+ ",lI wonder what other people think of me... Possibly terribly.",
+ "#...What would happen if I Vassalized a Clown?",
+ "#Why can't we just walk? Does anyone walk anymore? Why do we run? What rush are we in?",
+ "#Medbay is overworking, I wonder why they are always so shortstaffed.",
+ "#Why do we take a pod instead of the shuttle? Where's the fun in that?",
+ ",lHuh...",
+ "#I was so close to a new revelation, but I lost my train of thought for a moment there.",
+ "#What happens once all the organics die? Would just the unorganics remain?",
+ "#Hmmm... What would happen if I killed a Command member...",
+ "#Supermatters unnaturally look like candy... I wonder what would happen if I licked it...",
+ "#Everyone thinks of me as a freak, at least I'm not a creep, then they'd be sorry.",
+ ",lDon't forget to use the Mentor tab to ask for help!",
+ "#I wonder what they put on the bikes to cost a million credits...",
+ "#I wonder if my upstream would accept me for who I am.",
+ "#Is it possible... for the undead to get a heart attack? Nevermind, strange question.",
+ ",lWhat would happen if a Vampire got their hands on a Power Miner?",
+ "#There are Aliens, they exist. It isn't a conspiracy. The real question is when they will attack us.",
+ "#Is Brain damage real, or is it just our brains adapting to reality?",
+ "#How do we all understand eachother when we speak over eachother on the radio?",
+ "#Huds are broken again, it seems...",
+ "#Never make a deal with the devil... worst mistake of my life.",
+ "#Does plasma still affect the minds of people who can't get poisoned?",
+ "#Who thought sending a research station into a contested area was a good idea? Unless it's just sick and twisted humor... like a game!",
+ "#How well would a Cryogenic Blob deal against my power?",
+ "#It's possible to learn how to bloodcrawl...",
+ "#Changelings are the purest form of a Human... if Humans were the most unpure thing.",
+ "#I would like to take a stroll around the station, floating through the space around us... must feel nice.",
+ "#Who thought of the idea of Health Analyzers? Like, something that instantly knows everything wrong with you?",
+ "#How much of our soul does Nanotrasen REALLY own... they certainly don't own enough if they always have traitors among their crew...",
+ "#I wonder how the Devil is doing today... haven't seen them in a long time.",
+ "#We straight gassing cutting straight to the bricks ha ha.",
+ "#This shit ain't nothing to me man.",
+ "#I had to do it to them snipe.",
+ "#I'm not loyal to anybody I'm a demon.",
+ "#I have no loyalty for anyone never did never will.",
+ "#Shorty chose to be with a demon sounds like her problem to, me ha ha!",
+ "#Moving like Dracula we get it back in blood.",
+ "#You see it I really did this I'm really him.",
+ "#Flipped a whole brick into an empire stop playing with me.",
+ "#Smoking fentanyl-laced blood; I see God.",
+ "#Yeah we getting that Pirate Bay alien shish kabab cordycep money.",
+ "#I just popped a whole garbanzo bean, fuck you mean?",
+ "#I smoke real Emrānī rapscallion ghost nuggets.",
+ "I'm him! I been him!! I will continue to be him!!!",
+ "#They thought they could stop the demon, I'm back!",
+ "#The zaza got me speaking Esperanto.",
+ "#You can't trust me, I don't even trust myself. I don't even know who I am anymore, I'm getting too much money.",
+ "#Get the Captain on the holocall now! I fronted him a brick, I need my money!",
+ "#We smokin' Symbiotes.",
+ "#Smokin' that Whoopi Goldberg south Egyptian kindred deluxe Mega Millions scratcher skunk bubba kush.",
+ "#We smokin' Sequoia banshee boogers.",
+ "#They must have amnesia, they forgot that I'm him.",
+ "#Motherfucker look like a Resident Evil 5 campaign extra after we was done with him.",
+ "#Ops wanted some initiative, blew up their entire quadrant, I'm moving like Cuban Pete.",
+ "#I was flipping bricks for Mansa Musa before y'all even became a type 1 civilization.",
+ "#I have seen the Magna Carta. I've seen the Eye of Hora.",
+ "#You think I care about this shit? Ask me if I care about this shit, 'cause I don't give a shit! If I had a credit for every time they said I gave a shit, I'd be broke 'Cause I don't give a shit!",
+ "#This .357 got me moving like an invasive species.",
+ "#I got Midas touch shitter.",
+ "#I'm at the vault boutta withdraw all of it.",
+ "#That Fentanyl gave me Vitruvian Man flexibility. Got me in a state of rigor mortis.",
+ "#Caught a broke boy trying to come up on my Amazon package, so I skinned his ass alive.",
+ "#We smokin' Serge Ibaka spinal fluid infused quick-release percs.",
+ "#They needеd a stealth soldier, so I put my hands on the hibachi hot plate at Benihana, and burned my fucking finger prints off. They will not find me...",
+ "#Konichiwa you little jit.",
+ "#Snortin' some premium Matisyahu got me fightin' for my life.",
+ "#The Cuban link will turn the diamond tester into a pipe bomb.",
+ "#Stechkin shivered his timbers.",
+ "#I'm smoking Mesopotamian, Stanley Cup triple-award-winning, soul-bleeder, J.D. Power Associates, dingleberry zaza.",
+ "#We smoking that IBM Quantum Computer.",
+ "#My diamonds come from the most horrific situations possible.",
+ "#Fuck it, I ate the opp.",
+ "#Fuck it, I'm coming for every enzyme.",
+ "#I'll fucking kill you!.",
+ "#The first time I smoked runts, I coughed so fucking hard, I started passing kidney stones, then toolboxed myself in front of the gang!",
+ "#Hold on, lemme get some sip.",
+ "#The Codex should be treated like a Nuclear Authentication Disk, it is what guards this realm from the one below, afterall...",
+ "#No one knows how to read anymore, no matter how 'in your face' you put things, they'll never get it.",
+ "#150, 149, 148... 147, 146, 145, 144... What number was I at, again?",
+ "#No matter what we do, the feeling of pain will be inevitable.",
+ "#It seems Revolutionaries might take over the station today",
+ "#Huh, Nuclear Operatives lost in space. That's new.",
+ ",lWhere did I go wrong in my mortal life to end up here...",
+ "#The one that knows the Monster's tricks is sure to arrive. Only time will tell when.",
+ "#What are we even doing on such a Station? Don't we all know this will end in disaster?",
+ "#I can't think properly...",
+ "#I wonder what the Ancient Greek philosophers would say if they were alive today.",
+ "#I could go for some food just about now...",
+ "#Some coffee would be life-changing right about now...",
+ "#If only everyone saw the world in the same way I have",
+ "#What did Humanity do to deserve my creation?",
+ "#If we were all born for a reason, mine is completely idiotic.",
+ "#If there really is a God, why would they allow me to exist?",
+ "#...I think I lost track of something... I can't remember what...",
+ "#Who is humanity to decide who someone is? Why should they meddle in my affairs?",
+ "#The person everyone tries to silence, is the one people will miss the most",
+ "#It's hard to tell if people just don't understand my level of philosophy, or if they just play dumb to get reactions out of me.",
+ ";This is your fault.",
+ ",lWhy do we always infight, what's wrong with a little teamwork, it gets us further.",
+ "#What's a hacked autodrobe but a machine forced to show itself to you. Is it moral?"
+ ]
+}
diff --git a/tgui/packages/tgui/interfaces/AntagInfoRevengeVassal.tsx b/tgui/packages/tgui/interfaces/AntagInfoRevengeVassal.tsx
new file mode 100644
index 0000000000000..3bcf227719043
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AntagInfoRevengeVassal.tsx
@@ -0,0 +1,128 @@
+import { resolveAsset } from '../assets';
+import { BooleanLike } from 'common/react';
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Divider, Dropdown, Section, Stack } from '../components';
+import { Window } from '../layouts';
+
+type Objective = {
+ count: number;
+ name: string;
+ explanation: string;
+ complete: BooleanLike;
+ was_uncompleted: BooleanLike;
+ reward: number;
+};
+
+type VampireInformation = {
+ power: PowerInfo[];
+};
+
+type PowerInfo = {
+ power_name: string;
+ power_explanation: string;
+ power_icon: string;
+};
+
+type Info = {
+ objectives: Objective[];
+};
+
+const ObjectivePrintout = (props: any, context: any) => {
+ const { data } = useBackend(context);
+ const { objectives } = data;
+ return (
+
+ Your current objectives:
+
+ {(!objectives && 'None!') ||
+ objectives.map((objective) => (
+
+ #{objective.count}: {objective.explanation}
+
+ ))}
+
+
+ );
+};
+
+export const AntagInfoRevengeVassal = (props: any, context: any) => {
+ return (
+
+
+
+
+
+ );
+};
+
+const VassalInfo = () => {
+ return (
+
+
+
+
+
+ You are a Vassal tasked with taking revenge for the death of your Master!
+
+
+
+
+
+
+
+
+
+
+
+
+ You have gained your Master's old Powers, and a brand new power. You will have to survive and maintain your
+ old Master's integrity. Bring their old Vassals back into the fold using your new Ability.
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const PowerSection = (props: any, context: any) => {
+ const { act, data } = useBackend(context);
+ const { power } = data;
+ if (!power) {
+ return ;
+ }
+
+ const [selectedPower, setSelectedPower] = useLocalState(context, 'power', power[0]);
+
+ return (
+ }>
+
+
+ powers.power_name)}
+ onSelected={(powerName: string) => setSelectedPower(power.find((p) => p.power_name === powerName) || power[0])}
+ />
+ {selectedPower && (
+
+ )}
+
+
+
+
+ {selectedPower && selectedPower.power_explanation}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/AntagInfoVampire.tsx b/tgui/packages/tgui/interfaces/AntagInfoVampire.tsx
new file mode 100644
index 0000000000000..572616f960c9a
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AntagInfoVampire.tsx
@@ -0,0 +1,243 @@
+import { resolveAsset } from '../assets';
+import { BooleanLike } from 'common/react';
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Divider, Dropdown, Section, Stack, Tabs } from '../components';
+import { Window } from '../layouts';
+
+type Objective = {
+ count: number;
+ name: string;
+ explanation: string;
+ complete: BooleanLike;
+ was_uncompleted: BooleanLike;
+ reward: number;
+};
+
+type VampireInformation = {
+ clan: ClanInfo[];
+ in_clan: BooleanLike;
+ power: PowerInfo[];
+};
+
+type ClanInfo = {
+ clan_name: string;
+ clan_description: string;
+ clan_icon: string;
+};
+
+type PowerInfo = {
+ power_name: string;
+ power_explanation: string;
+ power_icon: string;
+};
+
+type Info = {
+ objectives: Objective[];
+};
+
+const ObjectivePrintout = (props: any, context: any) => {
+ const { data } = useBackend(context);
+ const { objectives } = data;
+ return (
+
+ Your current objectives:
+
+ {(!objectives && 'None!') ||
+ objectives.map((objective) => (
+
+ #{objective.count}: {objective.explanation}
+
+ ))}
+
+
+ );
+};
+
+export const AntagInfoVampire = (props: any, context: any) => {
+ const [tab, setTab] = useLocalState(context, 'tab', 1);
+ return (
+
+
+
+ setTab(1)}>
+ Introduction
+
+ setTab(2)}>
+ Clan & Powers
+
+
+ {tab === 1 && }
+ {tab === 2 && }
+
+
+ );
+};
+
+const VampireIntro = () => {
+ return (
+
+
+
+
+
+ You are a Vampire, an undead blood-seeking monster living aboard Space Station 13
+
+
+
+
+
+
+
+
+
+
+
+
+ You regenerate your health slowly, you're weak to fire, and you depend on blood to survive. Don't allow
+ your blood to run too low, or you'll enter a
+
+ Frenzy!
+
+
+ Avoid using your Feed ability while near mortals, or else you will risk breaking the Masquerade!
+
+
+
+
+
+
+
+
+
+ Rest in a Coffin to claim it, and that area, as your lair.
+
+ Examine your new structures to see how they function!
+
+ Medical and Genetic Analyzers can sell you out, your Masquerade ability will hide your identity to prevent this.
+
+
+
+
+ Other Vampires are not necessarily your friends, but your survival may depend on cooperation. Betray them at
+ your own discretion and peril.
+
+
+
+
+
+
+ );
+};
+
+const VampireClan = (props: any, context: any) => {
+ const { act, data } = useBackend(context);
+ const { clan, in_clan } = data;
+
+ if (!in_clan) {
+ return (
+
+
+ You are not in a Clan.
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {clan.map((ClanInfo) => (
+ <>
+
+
+ You are part of the {ClanInfo.clan_name}
+
+ {ClanInfo.clan_description}
+ >
+ ))}
+
+
+
+
+
+
+ );
+};
+
+const PowerSection = (props: any, context: any) => {
+ const { act, data } = useBackend(context);
+ const { power } = data;
+ if (!power) {
+ return ;
+ }
+
+ const [selectedPower, setSelectedPower] = useLocalState(context, 'power', power[0]);
+
+ return (
+
+ }>
+
+
+
+ powers.power_name)}
+ onSelected={(powerName: string) => setSelectedPower(power.find((p) => p.power_name === powerName) || power[0])}
+ />
+
+
+
+ {selectedPower && selectedPower.power_explanation}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/KindredBook.tsx b/tgui/packages/tgui/interfaces/KindredBook.tsx
new file mode 100644
index 0000000000000..306c567e9917e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/KindredBook.tsx
@@ -0,0 +1,41 @@
+import { useBackend } from '../backend';
+import { Collapsible, Table, Section } from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ clans: ClanInfo[];
+};
+
+type ClanInfo = {
+ clan_name: string;
+ clan_desc: string;
+};
+
+export const KindredBook = (props, context) => {
+ const { data } = useBackend(context);
+ const { clans } = data;
+ return (
+
+
+
+
+
+ Written by generations of Curators, this holds all information we the Curators know about the undead threat that
+ looms the station...
+
+ So, what Clan are you interested in?
+