Datumized vampires (#17166)

Co-authored-by: Farie82 <farie82@users.noreply.github.com>
Co-authored-by: dearmochi <1496804+dearmochi@users.noreply.github.com>
This commit is contained in:
Charlie
2022-01-18 20:16:09 +00:00
committed by GitHub
parent b2704e06b2
commit a0e5d6df44
33 changed files with 626 additions and 759 deletions
+16 -37
View File
@@ -53,7 +53,6 @@
var/list/antag_datums
var/datum/changeling/changeling //changeling holder
var/linglink
var/datum/vampire/vampire //vampire holder
var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state
var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD
@@ -116,8 +115,6 @@
for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body
var/datum/antagonist/A = a
A.on_body_transfer(old_current, current)
if(vampire)
vampire.update_owner(new_character)
transfer_antag_huds(hud_to_transfer) //inherit the antag HUD
transfer_actions(new_character)
if(martial_art)
@@ -294,7 +291,7 @@
/datum/mind/proc/memory_edit_vampire(mob/living/carbon/human/H)
. = _memory_edit_header("vampire", list("traitorvamp"))
if(src in SSticker.mode.vampires)
if(has_antag_datum(/datum/antagonist/vampire))
. += "<b><font color='red'>VAMPIRE</font></b>|<a href='?src=[UID()];vampire=clear'>no</a>"
if(objectives.len==0)
. += "<br>Objectives are empty! <a href='?src=[UID()];vampire=autoobjectives'>Randomize!</a>"
@@ -304,7 +301,7 @@
. += _memory_edit_role_enabled(ROLE_VAMPIRE)
/** Enthralled ***/
. += "<br><b><i>enthralled</i></b>: "
if(src in SSticker.mode.vampire_enthralled)
if(has_antag_datum(/datum/antagonist/mindslave/thrall))
. += "<b><font color='red'>THRALL</font></b>|<a href='?src=[UID()];vampthrall=clear'>no</a>"
else
. += "thrall|<b>NO</b>"
@@ -404,7 +401,7 @@
. += "<b>NO</b>"
// Mindslave
. += "<br><b><i>mindslaved</i></b>: "
if(has_antag_datum(/datum/antagonist/mindslave))
if(has_antag_datum(/datum/antagonist/mindslave, FALSE))
. += "<b><font color='red'>MINDSLAVE</font></b>|<a href='?src=[UID()];mindslave=clear'>no</a>"
else
. += "mindslave|<b>NO</b>"
@@ -1005,33 +1002,21 @@
else if(href_list["vampire"])
switch(href_list["vampire"])
if("clear")
if(src in SSticker.mode.vampires)
SSticker.mode.vampires -= src
special_role = null
if(vampire)
vampire.remove_vampire_powers()
qdel(vampire)
vampire = null
SSticker.mode.update_vampire_icons_removed(src)
if(has_antag_datum(/datum/antagonist/vampire))
remove_antag_datum(/datum/antagonist/vampire)
to_chat(current, "<FONT color='red' size = 3><B>You grow weak and lose your powers! You are no longer a vampire and are stuck in your current form!</B></FONT>")
log_admin("[key_name(usr)] has de-vampired [key_name(current)]")
message_admins("[key_name_admin(usr)] has de-vampired [key_name_admin(current)]")
if("vampire")
if(!(src in SSticker.mode.vampires))
SSticker.mode.vampires += src
SSticker.mode.grant_vampire_powers(current)
SSticker.mode.update_vampire_icons_added(src)
var/datum/mindslaves/slaved = new()
slaved.masters += src
som = slaved //we MIGT want to mindslave someone
special_role = SPECIAL_ROLE_VAMPIRE
SEND_SOUND(current, sound('sound/ambience/antag/vampalert.ogg'))
if(!has_antag_datum(/datum/antagonist/vampire))
add_antag_datum(/datum/antagonist/vampire)
to_chat(current, "<B><font color='red'>Your powers have awoken. Your lust for blood grows... You are a Vampire!</font></B>")
log_admin("[key_name(usr)] has vampired [key_name(current)]")
message_admins("[key_name_admin(usr)] has vampired [key_name_admin(current)]")
if("autoobjectives")
SSticker.mode.forge_vampire_objectives(src)
var/datum/antagonist/vampire/V = has_antag_datum(/datum/antagonist/vampire)
V.give_objectives()
to_chat(usr, "<span class='notice'>The objectives for vampire [key] have been generated. You can edit them and announce manually.</span>")
log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]")
message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]")
@@ -1039,8 +1024,8 @@
else if(href_list["vampthrall"])
switch(href_list["vampthrall"])
if("clear")
if(src in SSticker.mode.vampire_enthralled)
SSticker.mode.remove_vampire_mind(src)
if(has_antag_datum(/datum/antagonist/mindslave/thrall))
remove_antag_datum(/datum/antagonist/mindslave/thrall)
log_admin("[key_name(usr)] has de-vampthralled [key_name(current)]")
message_admins("[key_name_admin(usr)] has de-vampthralled [key_name_admin(current)]")
@@ -1345,7 +1330,7 @@
else if(href_list["mindslave"])
switch(href_list["mindslave"])
if("clear")
if(has_antag_datum(/datum/antagonist/mindslave))
if(has_antag_datum(/datum/antagonist/mindslave, FALSE))
var/mob/living/carbon/human/H = current
for(var/i in H.contents)
if(istype(i, /obj/item/implant/traitor))
@@ -1530,7 +1515,7 @@
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
if(!datum_type)
return
return FALSE
. = FALSE
for(var/a in antag_datums)
var/datum/antagonist/A = a
@@ -1592,15 +1577,9 @@
SSticker.mode.equip_syndicate(current)
/datum/mind/proc/make_vampire(ancient_vampire = FALSE)
if(!(src in SSticker.mode.vampires))
SSticker.mode.vampires += src
SSticker.mode.grant_vampire_powers(current)
special_role = SPECIAL_ROLE_VAMPIRE
SSticker.mode.greet_vampire(src)
SSticker.mode.update_vampire_icons_added(src)
if(!ancient_vampire)
SSticker.mode.forge_vampire_objectives(src)
/datum/mind/proc/make_vampire()
if(!has_antag_datum(/datum/antagonist/vampire))
add_antag_datum(/datum/antagonist/vampire)
/datum/mind/proc/make_Changeling()
if(!(src in SSticker.mode.changelings))
+5 -4
View File
@@ -1095,12 +1095,13 @@
apply_to_card(I, H, get_all_accesses(), "Ancient One", "data")
if(H.mind)
if(!H.mind.vampire)
if(!H.mind.has_antag_datum(/datum/antagonist/vampire))
H.mind.make_vampire(TRUE)
H.mind.vampire.bloodusable = 9999
H.mind.vampire.bloodtotal = 9999
var/datum/antagonist/vampire/V = H.mind.has_antag_datum(/datum/antagonist/vampire)
V.bloodusable = 9999
V.bloodtotal = 9999
H.mind.offstation_role = TRUE
H.mind.vampire.add_subclass(SUBCLASS_ANCIENT, FALSE)
V.add_subclass(SUBCLASS_ANCIENT, FALSE)
H.dna.SetSEState(GLOB.jumpblock, TRUE)
singlemutcheck(H, GLOB.jumpblock, MUTCHK_FORCED)
H.update_mutations()
+4 -4
View File
@@ -4,7 +4,7 @@
var/deduct_blood_on_cast = TRUE
/datum/spell_handler/vampire/can_cast(mob/user, charge_check, show_message, obj/effect/proc_holder/spell/spell)
var/datum/vampire/vampire = user.mind.vampire
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
if(!vampire)
return FALSE
@@ -35,11 +35,11 @@
if(!required_blood || !deduct_blood_on_cast) //don't take the blood yet if this is false!
return
var/datum/vampire/vampire = user.mind.vampire
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
vampire.bloodusable -= calculate_blood_cost(vampire)
/datum/spell_handler/vampire/proc/calculate_blood_cost(datum/vampire/vampire)
/datum/spell_handler/vampire/proc/calculate_blood_cost(datum/antagonist/vampire/vampire)
var/blood_cost_modifier = 1 + vampire.nullified / 100
var/blood_cost = round(required_blood * blood_cost_modifier)
return blood_cost
@@ -47,7 +47,7 @@
/datum/spell_handler/vampire/after_cast(list/targets, mob/user, obj/effect/proc_holder/spell/spell)
if(!required_blood)
return
var/datum/vampire/vampire = user.mind.vampire
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
to_chat(user, "<span class='boldnotice'>You have [vampire.bloodusable] left to use.</span>")
SSblackbox.record_feedback("tally", "vampire_powers_used", 1, "[spell]") // Only log abilities which require blood
+2 -1
View File
@@ -157,7 +157,8 @@
H.physiology.burn_mod *= 0.8
H.physiology.stamina_mod *= 0.5
H.physiology.stun_mod *= 0.5
if(owner.mind.vampire.get_ability(/datum/vampire_passive/blood_swell_upgrade))
var/datum/antagonist/vampire/V = owner.mind.has_antag_datum(/datum/antagonist/vampire)
if(V.get_ability(/datum/vampire_passive/blood_swell_upgrade))
bonus_damage_applied = TRUE
H.physiology.melee_bonus += 10
H.dna.species.punchstunthreshold += 8 //higher chance to stun but not 100%
+1 -1
View File
@@ -1745,7 +1745,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
else if(!AT)
to_chat(usr, "<span class='warning'>Error: Embedded Syndicate credentials not found.</span>")
return
else if(mind.changeling || mind.vampire)
else if(mind.changeling || mind.has_antag_datum(/datum/antagonist/vampire))
to_chat(usr, "<span class='warning'>Error: Embedded Syndicate credentials contain an abnormal signature. Aborting.</span>")
return
@@ -253,7 +253,7 @@
if(has_guardian(user))
to_chat(user, "You already have a [mob_name]!")
return
if(user.mind && (user.mind.changeling || user.mind.vampire))
if(user.mind && (user.mind.changeling || user.mind.has_antag_datum(/datum/antagonist/vampire)))
to_chat(user, "[ling_failure]")
return
if(used == TRUE)
+9 -3
View File
@@ -607,6 +607,11 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
return stolen_count >= 5
/datum/objective/blood
/datum/objective/blood/New()
gen_amount_goal()
. = ..()
/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400)
target_amount = rand(low,high)
target_amount = round(round(target_amount/5)*5)
@@ -614,10 +619,11 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
return target_amount
/datum/objective/blood/check_completion()
if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount)
return 1
var/datum/antagonist/vampire/V = owner.has_antag_datum(/datum/antagonist/vampire)
if(V.bloodtotal >= target_amount)
return TRUE
else
return 0
return FALSE
// Traders
+7 -16
View File
@@ -10,6 +10,7 @@
recommended_enemies = 3
secondary_enemies_scaling = 0.025
secondary_protected_species = list("Machine")
var/list/datum/mind/pre_vampires = list()
/datum/game_mode/traitor/vampire/announce()
to_chat(world, "<B>The current game mode is - Traitor+Vampire!</B>")
@@ -29,28 +30,18 @@
if(possible_vampires.len > 0)
for(var/I in possible_vampires)
if(length(vampires) >= secondary_enemies)
if(length(pre_vampires) >= secondary_enemies)
break
var/datum/mind/vampire = pick(possible_vampires)
vampires += vampire
modePlayer += vampires
possible_vampires -= vampire
var/datum/mindslaves/slaved = new()
slaved.masters += vampire
vampire.som = slaved //we MIGHT want to mindslave someone
vampire.restricted_roles = (restricted_jobs + secondary_restricted_jobs)
var/datum/mind/vampire = pick_n_take(possible_vampires)
pre_vampires += vampire
vampire.special_role = SPECIAL_ROLE_VAMPIRE
vampire.restricted_roles = (restricted_jobs + secondary_restricted_jobs)
..()
return 1
else
return 0
/datum/game_mode/traitor/vampire/post_setup()
for(var/datum/mind/vampire in vampires)
grant_vampire_powers(vampire.current)
vampire.special_role = SPECIAL_ROLE_VAMPIRE
forge_vampire_objectives(vampire)
greet_vampire(vampire)
update_vampire_icons_added(vampire)
for(var/datum/mind/vampire in pre_vampires)
vampire.add_antag_datum(/datum/antagonist/vampire)
..()
-535
View File
@@ -1,535 +0,0 @@
/datum/game_mode
var/list/datum/mind/vampires = list()
var/list/datum/mind/vampire_enthralled = list() //those controlled by a vampire
var/list/vampire_thralls = list() //vampires controlling somebody
/datum/game_mode/vampire
name = "vampire"
config_tag = "vampire"
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Chaplain", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation General")
protected_species = list("Machine")
required_players = 15
required_enemies = 1
recommended_enemies = 4
var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time
var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target
var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target
var/const/prob_int_item = 50 // intercept names the theft target half the time
var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target
var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target
var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time
var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target
var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target
var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative
var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative
var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly
var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly
var/vampire_amount = 4
/datum/game_mode/vampire/announce()
to_chat(world, "<B>The current game mode is - Vampires!</B>")
to_chat(world, "<B>There are Bluespace Vampires infesting your fellow crewmates, keep your blood close and neck safe!</B>")
/datum/game_mode/vampire/pre_setup()
if(GLOB.configuration.gamemode.prevent_mindshield_antags)
restricted_jobs += protected_jobs
var/list/datum/mind/possible_vampires = get_players_for_role(ROLE_VAMPIRE)
vampire_amount = 1 + round(num_players() / 10)
if(possible_vampires.len>0)
for(var/i = 0, i < vampire_amount, i++)
if(!possible_vampires.len) break
var/datum/mind/vampire = pick(possible_vampires)
possible_vampires -= vampire
vampires += vampire
vampire.restricted_roles = restricted_jobs
modePlayer += vampires
var/datum/mindslaves/slaved = new()
slaved.masters += vampire
vampire.som = slaved //we MIGT want to mindslave someone
vampire.special_role = SPECIAL_ROLE_VAMPIRE
..()
return 1
else
return 0
/datum/game_mode/vampire/post_setup()
for(var/datum/mind/vampire in vampires)
grant_vampire_powers(vampire.current)
forge_vampire_objectives(vampire)
greet_vampire(vampire)
update_vampire_icons_added(vampire)
..()
/datum/game_mode/proc/auto_declare_completion_vampire()
if(vampires.len)
var/text = "<FONT size = 2><B>The vampires were:</B></FONT>"
for(var/datum/mind/vampire in vampires)
var/traitorwin = 1
text += "<br>[vampire.key] was [vampire.name] ("
if(vampire.current)
if(vampire.current.stat == DEAD)
text += "died"
else
text += "survived"
if(vampire.vampire.subclass)
text += " as a [vampire.vampire.subclass.name]"
else
text += "body destroyed"
text += ")"
var/list/all_objectives = vampire.get_all_objectives()
if(length(all_objectives))//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in all_objectives)
if(objective.check_completion())
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='green'><B>Success!</B></font>"
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS"))
else
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='red'>Fail.</font>"
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "FAIL"))
traitorwin = 0
count++
var/special_role_text
if(vampire.special_role)
special_role_text = lowertext(vampire.special_role)
else
special_role_text = "antagonist"
if(traitorwin)
text += "<br><font color='green'><B>The [special_role_text] was successful!</B></font>"
SSblackbox.record_feedback("tally", "traitor_success", 1, "SUCCESS")
else
text += "<br><font color='red'><B>The [special_role_text] has failed!</B></font>"
SSblackbox.record_feedback("tally", "traitor_success", 1, "FAIL")
to_chat(world, text)
return 1
/datum/game_mode/proc/auto_declare_completion_enthralled()
if(vampire_enthralled.len)
var/text = "<FONT size = 2><B>The Enthralled were:</B></FONT>"
for(var/datum/mind/mind in vampire_enthralled)
text += "<br>[mind.key] was [mind.name] ("
if(mind.current)
if(mind.current.stat == DEAD)
text += "died"
else
text += "survived"
if(mind.current.real_name != mind.name)
text += " as [mind.current.real_name]"
else
text += "body destroyed"
text += ")"
to_chat(world, text)
return 1
/datum/game_mode/proc/forge_vampire_objectives(datum/mind/vampire)
//Objectives are traitor objectives plus blood objectives
var/datum/objective/blood/blood_objective = new
blood_objective.owner = vampire
blood_objective.gen_amount_goal(150, 400)
vampire.objectives += blood_objective
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = vampire
kill_objective.find_target()
vampire.objectives += kill_objective
var/datum/objective/steal/steal_objective = new
steal_objective.owner = vampire
steal_objective.find_target()
vampire.objectives += steal_objective
switch(rand(1,100))
if(1 to 80)
if(!(locate(/datum/objective/escape) in vampire.objectives))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = vampire
vampire.objectives += escape_objective
else
if(!(locate(/datum/objective/survive) in vampire.objectives))
var/datum/objective/survive/survive_objective = new
survive_objective.owner = vampire
vampire.objectives += survive_objective
return
/datum/game_mode/proc/grant_vampire_powers(mob/living/carbon/vampire_mob)
if(!istype(vampire_mob) || !vampire_mob.mind)
return
var/datum/vampire/vamp
if(!vampire_mob.mind.vampire)
vamp = new /datum/vampire()
vamp.owner = vampire_mob
vampire_mob.mind.vampire = vamp
else
vamp = vampire_mob.mind.vampire
QDEL_LIST(vamp.powers)
vamp.check_vampire_upgrade(FALSE)
/datum/game_mode/proc/greet_vampire(datum/mind/vampire, you_are=1)
var/dat
if(you_are)
SEND_SOUND(vampire.current, sound('sound/ambience/antag/vampalert.ogg'))
dat = "<span class='danger'>You are a Vampire!</span><br>"
dat += {"To bite someone, target the head and use harm intent with an empty hand. Drink blood to gain new powers.
You are weak to holy things, starlight and fire. Don't go into space and avoid the Chaplain, the chapel and especially Holy Water."}
to_chat(vampire.current, dat)
to_chat(vampire.current, "<B>You must complete the following tasks:</B>")
if(vampire.current.mind)
if(vampire.current.mind.assigned_role == "Clown")
to_chat(vampire.current, "Your lust for blood has allowed you to overcome your clumsy nature allowing you to wield weapons without harming yourself.")
vampire.current.dna.SetSEState(GLOB.clumsyblock, FALSE)
singlemutcheck(vampire.current, GLOB.clumsyblock, MUTCHK_FORCED)
var/datum/action/innate/toggle_clumsy/A = new
A.Grant(vampire.current)
var/obj_count = 1
for(var/datum/objective/objective in vampire.objectives)
to_chat(vampire.current, "<B>Objective #[obj_count]</B>: [objective.explanation_text]")
obj_count++
to_chat(vampire.current, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Vampire)</span>")
return
/datum/vampire
var/bloodtotal = 0
var/bloodusable = 0
/// the mob tied to the vampire
var/mob/living/owner = null
/// what vampire subclass the vampire is.
var/datum/vampire_subclass/subclass
/// handles the vampire cloak toggle
var/iscloaking = FALSE
/// list of available powers and passives
var/list/powers = list()
/// who the vampire is draining of blood
var/mob/living/carbon/human/draining
/// Nullrods and holywater make their abilities cost more
var/nullified = 0
/// a list of powers that all vampires unlock and at what blood level they unlock them, the rest of their powers are found in the vampire_subclass datum
var/list/upgrade_tiers = list(/obj/effect/proc_holder/spell/vampire/self/rejuvenate = 0,
/obj/effect/proc_holder/spell/vampire/glare = 0,
/datum/vampire_passive/vision = 100,
/obj/effect/proc_holder/spell/vampire/self/specialize = 150,
/datum/vampire_passive/regen = 200,
/obj/effect/proc_holder/spell/turf_teleport/shadow_step = 250)
/// list of the peoples UIDs that we have drained, and how much blood from each one
var/list/drained_humans = list()
/datum/vampire/Destroy(force, ...)
owner = null
draining = null
QDEL_NULL(subclass)
QDEL_LIST(powers)
return ..()
/datum/vampire/proc/adjust_nullification(base, extra)
// First hit should give full nullification, while subsequent hits increase the value slower
nullified = clamp(nullified + extra, base, VAMPIRE_NULLIFICATION_CAP)
/datum/vampire/proc/force_add_ability(path)
var/spell = new path(owner)
if(istype(spell, /obj/effect/proc_holder/spell))
owner.mind.AddSpell(spell)
if(istype(spell, /datum/vampire_passive))
var/datum/vampire_passive/passive = spell
passive.owner = owner
powers += spell
owner.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/vampire/proc/get_ability(path)
for(var/P in powers)
var/datum/power = P
if(power.type == path)
return power
return null
/datum/vampire/proc/add_ability(path)
if(!get_ability(path))
force_add_ability(path)
/datum/vampire/proc/remove_ability(ability)
if(ability && (ability in powers))
powers -= ability
owner.mind.spell_list.Remove(ability)
qdel(ability)
owner.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp loses his vision based powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/vampire/proc/update_owner(mob/living/carbon/human/current) //Called when a vampire gets cloned. This updates vampire.owner to the new body.
if(current.mind && current.mind.vampire && current.mind.vampire.owner && (current.mind.vampire.owner != current))
current.mind.vampire.owner = current
/datum/vampire/proc/remove_vampire_powers()
for(var/P in powers)
remove_ability(P)
if(owner.hud_used)
var/datum/hud/hud = owner.hud_used
if(hud.vampire_blood_display)
hud.remove_vampire_hud()
owner.alpha = 255
REMOVE_TRAITS_IN(owner, "vampire")
#define BLOOD_GAINED_MODIFIER 0.5
/datum/vampire/proc/handle_bloodsucking(mob/living/carbon/human/H, suck_rate = 5 SECONDS)
draining = H
var/unique_suck_id = H.UID()
var/blood = 0
var/blood_volume_warning = 9999 //Blood volume threshold for warnings
if(owner.is_muzzled())
to_chat(owner, "<span class='warning'>[owner.wear_mask] prevents you from biting [H]!</span>")
draining = null
return
add_attack_logs(owner, H, "vampirebit & is draining their blood.", ATKLOG_ALMOSTALL)
owner.visible_message("<span class='danger'>[owner] grabs [H]'s neck harshly and sinks in [owner.p_their()] fangs!</span>", "<span class='danger'>You sink your fangs into [H] and begin to drain [H.p_their()] blood.</span>", "<span class='notice'>You hear a soft puncture and a wet sucking noise.</span>")
if(!iscarbon(owner))
H.LAssailant = null
else
H.LAssailant = owner
while(do_mob(owner, H, suck_rate))
if(!(owner.mind in SSticker.mode.vampires))
to_chat(owner, "<span class='userdanger'>Your fangs have disappeared!</span>")
return
owner.do_attack_animation(H, ATTACK_EFFECT_BITE)
if(unique_suck_id in drained_humans)
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
to_chat(owner, "<span class='warning'>You have drained most of the life force from [H]'s blood, and you will get no more useable blood from them!</span>")
H.blood_volume = max(H.blood_volume - 25, 0)
owner.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.nutrition + 5))
continue
if(H.stat < DEAD)
if(H.ckey || H.player_ghosted) //Requires ckey regardless if monkey or humanoid, or the body has been ghosted before it died
blood = min(20, H.blood_volume)
adjust_blood(H, blood * BLOOD_GAINED_MODIFIER)
to_chat(owner, "<span class='notice'><b>You have accumulated [bloodtotal] unit\s of blood, and have [bloodusable] left to use.</b></span>")
H.blood_volume = max(H.blood_volume - 25, 0)
//Blood level warnings (Code 'borrowed' from Fulp)
if(H.blood_volume)
if(H.blood_volume <= BLOOD_VOLUME_BAD && blood_volume_warning > BLOOD_VOLUME_BAD)
to_chat(owner, "<span class='danger'>Your victim's blood volume is dangerously low.</span>")
else if(H.blood_volume <= BLOOD_VOLUME_OKAY && blood_volume_warning > BLOOD_VOLUME_OKAY)
to_chat(owner, "<span class='warning'>Your victim's blood is at an unsafe level.</span>")
blood_volume_warning = H.blood_volume //Set to blood volume, so that you only get the message once
else
to_chat(owner, "<span class='warning'>You have bled your victim dry!</span>")
break
if(!H.ckey && !H.player_ghosted)//Only runs if there is no ckey and the body has not being ghosted while alive
to_chat(owner, "<span class='notice'><b>Feeding on [H] reduces your thirst, but you get no usable blood from them.</b></span>")
owner.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.nutrition + 5))
else
owner.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.nutrition + (blood / 2)))
draining = null
to_chat(owner, "<span class='notice'>You stop draining [H.name] of blood.</span>")
#undef BLOOD_GAINED_MODIFIER
/datum/vampire/proc/check_vampire_upgrade(announce = TRUE)
var/list/old_powers = powers.Copy()
for(var/ptype in upgrade_tiers)
var/level = upgrade_tiers[ptype]
if(bloodtotal >= level)
add_ability(ptype)
if(!subclass)
return
subclass.add_subclass_ability(src)
check_full_power_upgrade()
if(announce)
announce_new_power(old_powers)
/datum/vampire/proc/check_full_power_upgrade()
if(length(drained_humans) >= FULLPOWER_DRAINED_REQUIREMENT && bloodtotal >= FULLPOWER_BLOODTOTAL_REQUIREMENT)
subclass.add_full_power_abilities(src)
/datum/vampire/proc/announce_new_power(list/old_powers)
for(var/p in powers)
if(!(p in old_powers))
if(istype(p, /obj/effect/proc_holder/spell))
var/obj/effect/proc_holder/spell/power = p
to_chat(owner, "<span class='boldnotice'>[power.gain_desc]</span>")
else if(istype(p, /datum/vampire_passive))
var/datum/vampire_passive/power = p
to_chat(owner, "<span class='boldnotice'>[power.gain_desc]</span>")
/datum/game_mode/proc/remove_vampire(datum/mind/vampire_mind)
if(vampire_mind in vampires)
SSticker.mode.vampires -= vampire_mind
vampire_mind.special_role = null
vampire_mind.current.create_attack_log("<span class='danger'>De-vampired</span>")
vampire_mind.current.create_log(CONVERSION_LOG, "De-vampired")
if(vampire_mind.vampire)
vampire_mind.vampire.remove_vampire_powers()
QDEL_NULL(vampire_mind.vampire)
if(issilicon(vampire_mind.current))
to_chat(vampire_mind.current, "<span class='userdanger'>You have been turned into a robot! You can feel your powers fading away...</span>")
else
to_chat(vampire_mind.current, "<span class='userdanger'>You have been brainwashed! You are no longer a vampire.</span>")
SSticker.mode.update_vampire_icons_removed(vampire_mind)
//prepare for copypaste
/datum/game_mode/proc/update_vampire_icons_added(datum/mind/vampire_mind)
var/datum/atom_hud/antag/vamp_hud = GLOB.huds[ANTAG_HUD_VAMPIRE]
vamp_hud.join_hud(vampire_mind.current)
set_antag_hud(vampire_mind.current, ((vampire_mind in vampires) ? "hudvampire" : "hudvampirethrall"))
/datum/game_mode/proc/update_vampire_icons_removed(datum/mind/vampire_mind)
var/datum/atom_hud/antag/vampire_hud = GLOB.huds[ANTAG_HUD_VAMPIRE]
vampire_hud.leave_hud(vampire_mind.current)
set_antag_hud(vampire_mind.current, null)
/datum/game_mode/proc/remove_vampire_mind(datum/mind/vampire_mind, datum/mind/head)
//var/list/removal
if(!istype(head))
head = vampire_mind //workaround for removing a thrall's control over the enthralled
var/ref = "\ref[head]"
if(ref in vampire_thralls)
vampire_thralls[ref] -= vampire_mind
vampire_enthralled -= vampire_mind
vampire_mind.special_role = null
var/datum/mindslaves/slaved = vampire_mind.som
slaved.serv -= vampire_mind
vampire_mind.som = null
slaved.leave_serv_hud(vampire_mind)
update_vampire_icons_removed(vampire_mind)
vampire_mind.current.visible_message("<span class='userdanger'>[vampire_mind.current] looks as though a burden has been lifted!</span>", "<span class='userdanger'>The dark fog in your mind clears as you regain control of your own faculties, you are no longer a vampire thrall!</span>")
if(vampire_mind.current.hud_used)
vampire_mind.current.hud_used.remove_vampire_hud()
/datum/vampire/proc/check_sun()
var/ax = owner.x
var/ay = owner.y
for(var/i = 1 to 20)
ax += SSsun.dx
ay += SSsun.dy
var/turf/T = locate(round(ax, 0.5), round(ay, 0.5), owner.z)
if(!T)
return
if(T.x == 1 || T.x == world.maxx || T.y == 1 || T.y == world.maxy)
break
if(T.density)
return
if(bloodusable >= 10) //burn through your blood to tank the light for a little while
to_chat(owner, "<span class='warning'>The starlight saps your strength!</span>")
bloodusable -= 10
vamp_burn(10)
else //You're in trouble, get out of the sun NOW
to_chat(owner, "<span class='userdanger'>Your body is turning to ash, get out of the light now!</span>")
owner.adjustCloneLoss(10) //I'm melting!
vamp_burn(85)
if(owner.cloneloss >= 100)
owner.dust()
/datum/vampire/proc/handle_vampire()
if(owner.hud_used)
var/datum/hud/hud = owner.hud_used
if(!hud.vampire_blood_display)
hud.vampire_blood_display = new /obj/screen()
hud.vampire_blood_display.name = "Usable Blood"
hud.vampire_blood_display.icon_state = "blood_display"
hud.vampire_blood_display.screen_loc = "WEST:6,CENTER-1:15"
hud.static_inventory += hud.vampire_blood_display
hud.show_hud(hud.hud_version)
hud.vampire_blood_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font face='Small Fonts' color='#ce0202'>[bloodusable]</font></div>"
handle_vampire_cloak()
if(istype(get_turf(owner), /turf/space))
check_sun()
if(istype(get_area(owner), /area/chapel) && !get_ability(/datum/vampire_passive/full))
vamp_burn(7)
nullified = max(0, nullified - 2)
/datum/vampire/proc/handle_vampire_cloak()
if(!ishuman(owner))
owner.alpha = 255
return
var/turf/simulated/T = get_turf(owner)
var/light_available = T.get_lumcount() * 10
if(!istype(T))
return
if(!iscloaking || owner.on_fire)
owner.alpha = 255
REMOVE_TRAIT(owner, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
return
if(light_available <= 2)
owner.alpha = 38 // round(255 * 0.15)
ADD_TRAIT(owner, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
return
REMOVE_TRAIT(owner, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
owner.alpha = 204 // 255 * 0.80
/datum/vampire/proc/adjust_blood(mob/living/carbon/C, blood_amount = 0)
if(C)
var/unique_suck_id = C.UID()
if(!(unique_suck_id in drained_humans))
drained_humans[unique_suck_id] = 0
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
return
drained_humans[unique_suck_id] += blood_amount
bloodtotal += blood_amount
bloodusable += blood_amount
check_vampire_upgrade(TRUE)
for(var/obj/effect/proc_holder/spell/S in powers)
if(S.action)
S.action.UpdateButtonIcon()
/datum/vampire/proc/vamp_burn(burn_chance)
if(prob(burn_chance) && owner.health >= 50)
switch(owner.health)
if(75 to 100)
to_chat(owner, "<span class='warning'>Your skin flakes away...</span>")
if(50 to 75)
to_chat(owner, "<span class='warning'>Your skin sizzles!</span>")
owner.adjustFireLoss(3)
else if(owner.health < 50)
if(!owner.on_fire)
to_chat(owner, "<span class='danger'>Your skin catches fire!</span>")
owner.emote("scream")
else
to_chat(owner, "<span class='danger'>You continue to burn!</span>")
owner.adjust_fire_stacks(5)
owner.IgniteMob()
return
/datum/hud/proc/remove_vampire_hud()
if(!vampire_blood_display)
return
static_inventory -= vampire_blood_display
QDEL_NULL(vampire_blood_display)
show_hud(hud_version)
/datum/vampire/vv_edit_var(var_name, var_value)
. = ..()
check_vampire_upgrade(TRUE)
@@ -0,0 +1,118 @@
/datum/game_mode
var/list/datum/mind/vampires = list()
var/list/datum/mind/vampire_enthralled = list() //those controlled by a vampire
/datum/game_mode/vampire
name = "vampire"
config_tag = "vampire"
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Chaplain", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation General")
protected_species = list("Machine")
required_players = 15
required_enemies = 1
recommended_enemies = 4
///list of minds of soon to be vampires
var/list/datum/mind/pre_vampires = list()
/datum/game_mode/vampire/announce()
to_chat(world, "<B>The current game mode is - Vampires!</B>")
to_chat(world, "<B>There are Bluespace Vampires infesting your fellow crewmates, keep your blood close and neck safe!</B>")
/datum/game_mode/vampire/pre_setup()
if(GLOB.configuration.gamemode.prevent_mindshield_antags)
restricted_jobs += protected_jobs
var/list/datum/mind/possible_vampires = get_players_for_role(ROLE_VAMPIRE)
var/vampire_amount = 1 + round(num_players() / 10)
if(length(possible_vampires))
for(var/i in 1 to vampire_amount)
if(!length(possible_vampires))
break
var/datum/mind/vampire = pick_n_take(possible_vampires)
pre_vampires += vampire
vampire.special_role = SPECIAL_ROLE_VAMPIRE
vampire.restricted_roles = restricted_jobs
..()
return TRUE
else
return FALSE
/datum/game_mode/vampire/post_setup()
for(var/datum/mind/vampire in pre_vampires)
vampire.add_antag_datum(/datum/antagonist/vampire)
..()
/datum/game_mode/proc/auto_declare_completion_vampire()
if(!length(vampires))
return
var/text = "<FONT size = 2><B>The vampires were:</B></FONT>"
for(var/datum/mind/vampire in vampires)
var/traitorwin = TRUE
var/datum/antagonist/vampire/V = vampire.has_antag_datum(/datum/antagonist/vampire)
text += "<br>[vampire.key] was [vampire.name] ("
if(vampire.current)
if(vampire.current.stat == DEAD)
text += "died"
else
text += "survived"
if(V.subclass)
text += " as a [V.subclass.name]"
else
text += "body destroyed"
text += ")"
var/list/all_objectives = vampire.get_all_objectives()
if(length(all_objectives))//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in all_objectives)
if(objective.check_completion())
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='green'><B>Success!</B></font>"
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS"))
else
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='red'>Fail.</font>"
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "FAIL"))
traitorwin = FALSE
count++
var/special_role_text
if(vampire.special_role)
special_role_text = lowertext(vampire.special_role)
else
special_role_text = "antagonist"
if(traitorwin)
text += "<br><font color='green'><B>The [special_role_text] was successful!</B></font>"
SSblackbox.record_feedback("tally", "traitor_success", 1, "SUCCESS")
else
text += "<br><font color='red'><B>The [special_role_text] has failed!</B></font>"
SSblackbox.record_feedback("tally", "traitor_success", 1, "FAIL")
to_chat(world, text)
return TRUE
/datum/game_mode/proc/auto_declare_completion_enthralled()
if(!length(vampire_enthralled))
return
var/text = "<FONT size = 2><B>The Enthralled were:</B></FONT>"
for(var/datum/mind/mind in vampire_enthralled)
text += "<br>[mind.key] was [mind.name] ("
if(mind.current)
if(mind.current.stat == DEAD)
text += "died"
else
text += "survived"
if(mind.current.real_name != mind.name)
text += " as [mind.current.real_name]"
else
text += "body destroyed"
text += ")"
to_chat(world, text)
return TRUE
-4
View File
@@ -444,10 +444,6 @@ GLOBAL_LIST_INIT(cloner_biomass_items, list(\
SSticker.mode.rise(H)
if(SSticker.mode.cult_ascendant)
SSticker.mode.ascend(H)
if(H.mind.vampire)
H.mind.vampire.update_owner(H)
if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled))
SSticker.mode.update_vampire_icons_added(H.mind)
if(H.mind in SSticker.mode.changelings)
SSticker.mode.update_change_icons_added(H.mind)
if((H.mind in SSticker.mode.shadowling_thralls) || (H.mind in SSticker.mode.shadows))
+6 -4
View File
@@ -150,8 +150,10 @@
else
do_animate("deny")
if(HAS_TRAIT(user, TRAIT_FORCE_DOORS))
if(user.mind?.vampire && HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
if(!user.mind.vampire.bloodusable)
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire)
if(V && HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
if(!V.bloodusable)
REMOVE_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)
return
if(welded)
@@ -164,8 +166,8 @@
visible_message("<span class='danger'>[user] forces the door open!</span>")
playsound(loc, "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
open(TRUE)
if(user.mind?.vampire && HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
user.mind.vampire.bloodusable = max(user.mind.vampire.bloodusable - 5, 0)
if(V && HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
V.bloodusable = max(V.bloodusable - 5, 0)
/obj/machinery/door/attack_ai(mob/user)
return attack_hand(user)
@@ -36,10 +36,11 @@
/obj/item/nullrod/attack(mob/M, mob/living/carbon/user)
..()
if(ishuman(M) && M.mind?.vampire && user.mind.isholy)
if(!M.mind.vampire.get_ability(/datum/vampire_passive/full))
var/datum/antagonist/vampire/V = M.mind?.has_antag_datum(/datum/antagonist/vampire)
if(ishuman(M) && V && user.mind.isholy)
if(!V.get_ability(/datum/vampire_passive/full))
to_chat(M, "<span class='warning'>The nullrod's power interferes with your own!</span>")
M.mind.vampire.adjust_nullification(30 + sanctify_force, 15 + sanctify_force)
V.adjust_nullification(30 + sanctify_force, 15 + sanctify_force)
/obj/item/nullrod/pickup(mob/living/user)
. = ..()
@@ -477,9 +478,9 @@
SSticker.mode.remove_cultist(target.mind, TRUE, TRUE) // This proc will handle message generation.
praying = FALSE
return
if(target.mind.vampire && !target.mind.vampire.get_ability(/datum/vampire_passive/full)) // Getting a full prayer off on a vampire will interrupt their powers for a large duration.
target.mind.vampire.adjust_nullification(120, 50)
var/datum/antagonist/vampire/V = M.mind?.has_antag_datum(/datum/antagonist/vampire)
if(V?.get_ability(/datum/vampire_passive/full)) // Getting a full prayer off on a vampire will interrupt their powers for a large duration.
V.adjust_nullification(120, 50)
to_chat(target, "<span class='userdanger'>[user]'s prayer to [SSticker.Bible_deity_name] has interfered with your power!</span>")
praying = FALSE
return
+2 -2
View File
@@ -651,9 +651,9 @@ GLOBAL_VAR_INIT(nologevent, 0)
antag_list += "Changeling"
if(M.mind in SSticker.mode.abductors)
antag_list += "Abductor"
if(M.mind in SSticker.mode.vampires)
if(M.mind.has_antag_datum(/datum/antagonist/vampire))
antag_list += "Vampire"
if(M.mind in SSticker.mode.vampire_enthralled)
if(M.mind.has_antag_datum(/datum/antagonist/mindslave/thrall))
antag_list += "Vampire Thrall"
if(M.mind in SSticker.mode.shadows)
antag_list += "Shadowling"
@@ -38,7 +38,7 @@
// Update our master's HUD to give him the "M" icon.
// Basically a copy and paste of what's in [/datum/antagonist/proc/add_antag_hud] in case the master doesn't have a traitor datum.
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_TRAITOR]
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.join_hud(master.current)
set_antag_hud(master.current, "hudmaster")
slaved.add_serv_hud(master, "master")
@@ -0,0 +1,322 @@
/datum/antagonist/vampire
name = "Vampire"
antag_hud_type = ANTAG_HUD_VAMPIRE
antag_hud_name = "hudvampire"
special_role = SPECIAL_ROLE_VAMPIRE
wiki_page_name = "Vampire"
var/bloodtotal = 0
var/bloodusable = 0
/// what vampire subclass the vampire is.
var/datum/vampire_subclass/subclass
/// handles the vampire cloak toggle
var/iscloaking = FALSE
/// list of available powers and passives
var/list/powers = list()
/// who the vampire is draining of blood
var/mob/living/carbon/human/draining
/// Nullrods and holywater make their abilities cost more
var/nullified = 0
/// a list of powers that all vampires unlock and at what blood level they unlock them, the rest of their powers are found in the vampire_subclass datum
var/list/upgrade_tiers = list(/obj/effect/proc_holder/spell/vampire/self/rejuvenate = 0,
/obj/effect/proc_holder/spell/vampire/glare = 0,
/datum/vampire_passive/vision = 100,
/obj/effect/proc_holder/spell/vampire/self/specialize = 150,
/datum/vampire_passive/regen = 200,
/obj/effect/proc_holder/spell/turf_teleport/shadow_step = 250)
/// list of the peoples UIDs that we have drained, and how much blood from each one
var/list/drained_humans = list()
/datum/antagonist/mindslave/thrall
name = "Vampire Thrall"
antag_hud_type = ANTAG_HUD_VAMPIRE
antag_hud_name = "vampthrall"
/datum/antagonist/mindslave/thrall/on_gain()
SSticker.mode.vampire_enthralled += owner
..()
/datum/antagonist/mindslave/thrall/on_removal()
SSticker.mode.vampire_enthralled -= owner
..()
/datum/antagonist/vampire/Destroy(force, ...)
draining = null
QDEL_NULL(subclass)
QDEL_LIST(powers)
return ..()
/datum/antagonist/vampire/proc/adjust_nullification(base, extra)
// First hit should give full nullification, while subsequent hits increase the value slower
nullified = clamp(nullified + extra, base, VAMPIRE_NULLIFICATION_CAP)
/datum/antagonist/vampire/proc/force_add_ability(path)
var/spell = new path(owner)
if(istype(spell, /obj/effect/proc_holder/spell))
owner.AddSpell(spell)
if(istype(spell, /datum/vampire_passive))
var/datum/vampire_passive/passive = spell
passive.owner = owner.current
powers += spell
owner.current.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/antagonist/vampire/proc/get_ability(path)
for(var/datum/power as anything in powers)
if(power.type == path)
return power
return null
/datum/antagonist/vampire/proc/add_ability(path)
if(!get_ability(path))
force_add_ability(path)
/datum/antagonist/vampire/proc/remove_ability(ability)
if(ability && (ability in powers))
powers -= ability
owner.spell_list.Remove(ability)
qdel(ability)
owner.current.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp loses his vision based powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/antagonist/vampire/remove_innate_effects(mob/living/old_body)
var/mob/living/L = old_body || owner.current
for(var/P in powers)
remove_ability(P)
var/datum/hud/hud = L.hud_used
if(hud?.vampire_blood_display)
hud.remove_vampire_hud()
owner.current.alpha = 255
REMOVE_TRAITS_IN(owner.current, "vampire")
return ..()
#define BLOOD_GAINED_MODIFIER 0.5
/datum/antagonist/vampire/proc/handle_bloodsucking(mob/living/carbon/human/H, suck_rate = 5 SECONDS)
draining = H
var/unique_suck_id = H.UID()
var/blood = 0
var/blood_volume_warning = 9999 //Blood volume threshold for warnings
if(owner.current.is_muzzled())
to_chat(owner.current, "<span class='warning'>[owner.current.wear_mask] prevents you from biting [H]!</span>")
draining = null
return
add_attack_logs(owner.current, H, "vampirebit & is draining their blood.", ATKLOG_ALMOSTALL)
owner.current.visible_message("<span class='danger'>[owner] grabs [H]'s neck harshly and sinks in [owner.current.p_their()] fangs!</span>", "<span class='danger'>You sink your fangs into [H] and begin to drain [H.p_their()] blood.</span>", "<span class='notice'>You hear a soft puncture and a wet sucking noise.</span>")
if(!iscarbon(owner.current))
H.LAssailant = null
else
H.LAssailant = owner
while(do_mob(owner.current, H, suck_rate))
owner.current.do_attack_animation(H, ATTACK_EFFECT_BITE)
if(unique_suck_id in drained_humans)
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
to_chat(owner.current, "<span class='warning'>You have drained most of the life force from [H]'s blood, and you will get no more useable blood from them!</span>")
H.blood_volume = max(H.blood_volume - 25, 0)
owner.current.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.current.nutrition + 5))
continue
if(H.stat < DEAD)
if(H.ckey || H.player_ghosted) //Requires ckey regardless if monkey or humanoid, or the body has been ghosted before it died
blood = min(20, H.blood_volume)
adjust_blood(H, blood * BLOOD_GAINED_MODIFIER)
to_chat(owner.current, "<span class='notice'><b>You have accumulated [bloodtotal] unit\s of blood, and have [bloodusable] left to use.</b></span>")
H.blood_volume = max(H.blood_volume - 25, 0)
//Blood level warnings (Code 'borrowed' from Fulp)
if(H.blood_volume)
if(H.blood_volume <= BLOOD_VOLUME_BAD && blood_volume_warning > BLOOD_VOLUME_BAD)
to_chat(owner.current, "<span class='danger'>Your victim's blood volume is dangerously low.</span>")
else if(H.blood_volume <= BLOOD_VOLUME_OKAY && blood_volume_warning > BLOOD_VOLUME_OKAY)
to_chat(owner.current, "<span class='warning'>Your victim's blood is at an unsafe level.</span>")
blood_volume_warning = H.blood_volume //Set to blood volume, so that you only get the message once
else
to_chat(owner.current, "<span class='warning'>You have bled your victim dry!</span>")
break
if(!H.ckey && !H.player_ghosted)//Only runs if there is no ckey and the body has not being ghosted while alive
to_chat(owner.current, "<span class='notice'><b>Feeding on [H] reduces your thirst, but you get no usable blood from them.</b></span>")
owner.current.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.current.nutrition + 5))
else
owner.current.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.current.nutrition + (blood / 2)))
draining = null
to_chat(owner.current, "<span class='notice'>You stop draining [H.name] of blood.</span>")
#undef BLOOD_GAINED_MODIFIER
/datum/antagonist/vampire/proc/check_vampire_upgrade(announce = TRUE)
var/list/old_powers = powers.Copy()
for(var/ptype in upgrade_tiers)
var/level = upgrade_tiers[ptype]
if(bloodtotal >= level)
add_ability(ptype)
if(!subclass)
return
subclass.add_subclass_ability(src)
check_full_power_upgrade()
if(announce)
announce_new_power(old_powers)
/datum/antagonist/vampire/proc/check_full_power_upgrade()
if(length(drained_humans) >= FULLPOWER_DRAINED_REQUIREMENT && bloodtotal >= FULLPOWER_BLOODTOTAL_REQUIREMENT)
subclass.add_full_power_abilities(src)
/datum/antagonist/vampire/proc/announce_new_power(list/old_powers)
for(var/p in powers)
if(!(p in old_powers))
if(istype(p, /obj/effect/proc_holder/spell))
var/obj/effect/proc_holder/spell/power = p
to_chat(owner.current, "<span class='boldnotice'>[power.gain_desc]</span>")
else if(istype(p, /datum/vampire_passive))
var/datum/vampire_passive/power = p
to_chat(owner.current, "<span class='boldnotice'>[power.gain_desc]</span>")
/datum/antagonist/vampire/on_removal()
SSticker.mode.vampires -= owner
owner.current.create_log(CONVERSION_LOG, "De-vampired")
..()
/datum/antagonist/vampire/on_gain()
SSticker.mode.vampires += owner
..()
/datum/antagonist/vampire/proc/check_sun()
var/ax = owner.current.x
var/ay = owner.current.y
for(var/i = 1 to 20)
ax += SSsun.dx
ay += SSsun.dy
var/turf/T = locate(round(ax, 0.5), round(ay, 0.5), owner.current.z)
if(!T)
return
if(T.x == 1 || T.x == world.maxx || T.y == 1 || T.y == world.maxy)
break
if(T.density)
return
if(bloodusable >= 10) //burn through your blood to tank the light for a little while
to_chat(owner.current, "<span class='warning'>The starlight saps your strength!</span>")
bloodusable -= 10
vamp_burn(10)
else //You're in trouble, get out of the sun NOW
to_chat(owner.current, "<span class='userdanger'>Your body is turning to ash, get out of the light now!</span>")
owner.current.adjustCloneLoss(10) //I'm melting!
vamp_burn(85)
if(owner.current.cloneloss >= 100)
owner.current.dust()
/datum/antagonist/vampire/proc/handle_vampire()
if(owner.current.hud_used)
var/datum/hud/hud = owner.current.hud_used
if(!hud.vampire_blood_display)
hud.vampire_blood_display = new /obj/screen()
hud.vampire_blood_display.name = "Usable Blood"
hud.vampire_blood_display.icon_state = "blood_display"
hud.vampire_blood_display.screen_loc = "WEST:6,CENTER-1:15"
hud.static_inventory += hud.vampire_blood_display
hud.show_hud(hud.hud_version)
hud.vampire_blood_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font face='Small Fonts' color='#ce0202'>[bloodusable]</font></div>"
handle_vampire_cloak()
if(istype(get_turf(owner.current), /turf/space))
check_sun()
if(istype(get_area(owner.current), /area/chapel) && !get_ability(/datum/vampire_passive/full))
vamp_burn(7)
nullified = max(0, nullified - 2)
/datum/antagonist/vampire/proc/handle_vampire_cloak()
if(!ishuman(owner.current))
owner.current.alpha = 255
return
var/turf/simulated/T = get_turf(owner.current)
var/light_available = T.get_lumcount() * 10
if(!istype(T))
return
if(!iscloaking || owner.current.on_fire)
owner.current.alpha = 255
REMOVE_TRAIT(owner.current, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
return
if(light_available <= 2)
owner.current.alpha = 38 // round(255 * 0.15)
ADD_TRAIT(owner.current, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
return
REMOVE_TRAIT(owner.current, TRAIT_GOTTAGONOTSOFAST, VAMPIRE_TRAIT)
owner.current.alpha = 204 // 255 * 0.80
/datum/antagonist/vampire/proc/adjust_blood(mob/living/carbon/C, blood_amount = 0)
if(C)
var/unique_suck_id = C.UID()
if(!(unique_suck_id in drained_humans))
drained_humans[unique_suck_id] = 0
if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT)
return
drained_humans[unique_suck_id] += blood_amount
bloodtotal += blood_amount
bloodusable += blood_amount
check_vampire_upgrade(TRUE)
for(var/obj/effect/proc_holder/spell/S in powers)
if(S.action)
S.action.UpdateButtonIcon()
/datum/antagonist/vampire/proc/vamp_burn(burn_chance)
if(prob(burn_chance) && owner.current.health >= 50)
switch(owner.current.health)
if(75 to 100)
to_chat(owner.current, "<span class='warning'>Your skin flakes away...</span>")
if(50 to 75)
to_chat(owner.current, "<span class='warning'>Your skin sizzles!</span>")
owner.current.adjustFireLoss(3)
else if(owner.current.health < 50)
if(!owner.current.on_fire)
to_chat(owner.current, "<span class='danger'>Your skin catches fire!</span>")
owner.current.emote("scream")
else
to_chat(owner.current, "<span class='danger'>You continue to burn!</span>")
owner.current.adjust_fire_stacks(5)
owner.current.IgniteMob()
/datum/antagonist/vampire/vv_edit_var(var_name, var_value)
. = ..()
check_vampire_upgrade(TRUE)
/datum/antagonist/vampire/give_objectives()
add_objective(/datum/objective/blood)
add_objective(/datum/objective/assassinate)
add_objective(/datum/objective/steal)
switch(rand(1, 100))
if(1 to 80)
add_objective(/datum/objective/survive)
else
add_objective(/datum/objective/escape)
/datum/antagonist/vampire/greet()
var/dat
SEND_SOUND(owner.current, sound('sound/ambience/antag/vampalert.ogg'))
dat = "<span class='danger'>You are a Vampire!</span><br>"
dat += {"To bite someone, target the head and use harm intent with an empty hand. Drink blood to gain new powers.
You are weak to holy things, starlight and fire. Don't go into space and avoid the Chaplain, the chapel and especially Holy Water."}
to_chat(owner.current, dat)
/datum/antagonist/vampire/apply_innate_effects(mob/living/new_body)
. = ..()
if(!owner.som) //thralls and mindslaves
owner.som = new()
owner.som.masters += owner
check_vampire_upgrade(FALSE)
/datum/hud/proc/remove_vampire_hud()
static_inventory -= vampire_blood_display
QDEL_NULL(vampire_blood_display)
@@ -65,7 +65,6 @@
return FALSE
return ..()
/obj/effect/proc_holder/spell/vampire/charge/cast(list/targets, mob/user)
var/target = targets[1]
if(isliving(user))
@@ -43,13 +43,16 @@
var/blood_absorbed_amount = 5
/obj/item/twohanded/required/vamp_claws/afterattack(atom/target, mob/user, proximity)
if(!user.mind?.vampire)
var/datum/antagonist/vampire/V = user.mind?.has_antag_datum(/datum/antagonist/vampire)
if(!V)
return
if(iscarbon(target))
var/mob/living/carbon/C = target
if(C.ckey && C.stat != DEAD && C.affects_vampire() && !(NO_BLOOD in C.dna.species.species_traits))
C.bleed(blood_drain_amount)
user.mind.vampire.adjust_blood(C, blood_absorbed_amount)
V.adjust_blood(C, blood_absorbed_amount)
durability -= 1
if(durability <= 0)
qdel(src)
@@ -89,9 +92,6 @@
required_blood = 10
charge_max = 30 SECONDS
panel = "Vampire"
school = "vampire"
action_background_icon_state = "bg_vampire"
action_icon_state = "blood_tendrils"
sound = 'sound/misc/enter_blood.ogg'
var/area_of_affect = 1
@@ -105,7 +105,6 @@
T.try_auto_target = FALSE
return T
/obj/effect/proc_holder/spell/vampire/blood_tendrils/cast(list/targets, mob/user)
var/turf/T = get_turf(targets[1]) // there should only ever be one entry in targets for this spell
@@ -174,6 +173,7 @@
return TRUE
return FALSE
/obj/effect/proc_holder/spell/vampire/blood_eruption/cast(list/targets, mob/user)
for(var/mob/living/L in targets)
var/turf/T = get_turf(L)
@@ -198,12 +198,12 @@
required_blood = 10
/obj/effect/proc_holder/spell/vampire/self/blood_spill/cast(list/targets, mob/user)
var/mob/target = targets[1]
if(!target.mind.vampire.get_ability(/datum/vampire_passive/blood_spill))
target.mind.vampire.force_add_ability(/datum/vampire_passive/blood_spill)
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire)
if(!V.get_ability(/datum/vampire_passive/blood_spill))
V.force_add_ability(/datum/vampire_passive/blood_spill)
else
for(var/datum/vampire_passive/blood_spill/B in target.mind.vampire.powers)
target.mind.vampire.remove_ability(B)
for(var/datum/vampire_passive/blood_spill/B in V.powers)
V.remove_ability(B)
/datum/vampire_passive/blood_spill
var/max_beams = 10
@@ -219,6 +219,7 @@
/datum/vampire_passive/blood_spill/process()
var/beam_number = 0
var/turf/T = get_turf(owner)
var/datum/antagonist/vampire/V = owner.mind.has_antag_datum(/datum/antagonist/vampire)
for(var/mob/living/carbon/human/H in view(7, T))
if(NO_BLOOD in H.dna.species.species_traits)
continue
@@ -240,6 +241,6 @@
if(beam_number >= max_beams)
break
owner.mind.vampire.bloodusable = max(owner.mind.vampire.bloodusable - 10, 0)
if(!owner.mind.vampire.bloodusable || owner.stat == DEAD)
owner.mind.vampire.remove_ability(src)
V.bloodusable = max(V.bloodusable - 10, 0)
if(!V.bloodusable || owner.stat == DEAD)
V.remove_ability(src)
@@ -11,12 +11,15 @@
/obj/effect/proc_holder/spell/vampire/self/cloak/proc/update_name()
var/mob/living/user = loc
if(!ishuman(user) || !user.mind || !user.mind.vampire)
if(!ishuman(user) || !user.mind)
return
action.button.name = "[initial(name)] ([user.mind.vampire.iscloaking ? "Deactivate" : "Activate"])"
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire)
if(!V)
return
action.button.name = "[initial(name)] ([V.iscloaking ? "Deactivate" : "Activate"])"
/obj/effect/proc_holder/spell/vampire/self/cloak/cast(list/targets, mob/user = usr)
var/datum/vampire/V = user.mind.vampire
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire)
V.iscloaking = !V.iscloaking
if(ishuman(user))
var/mob/living/carbon/human/H = user
@@ -32,7 +35,8 @@
/mob/living/proc/update_vampire_cloak()
SIGNAL_HANDLER
mind.vampire.handle_vampire_cloak()
var/datum/antagonist/vampire/V = mind.has_antag_datum(/datum/antagonist/vampire)
V.handle_vampire_cloak()
/obj/effect/proc_holder/spell/vampire/shadow_snare
name = "Shadow Snare (20)"
@@ -140,9 +144,6 @@
var/datum/spell_targeting/aoe/turf/T = new
return T
/obj/effect/proc_holder/spell/vampire/write_custom_logs(list/targets, mob/user)
add_attack_logs(user, null, "Extinguished all lights around them using [src]", ATKLOG_ALL)
/obj/effect/proc_holder/spell/vampire/vamp_extinguish/cast(list/targets, mob/user = usr)
for(var/turf/T in targets)
T.extinguish_light()
@@ -159,13 +160,14 @@
var/shroud_power = -4
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness/cast(list/targets, mob/user)
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire)
var/mob/target = targets[1]
if(!target.mind.vampire.get_ability(/datum/vampire_passive/eternal_darkness))
target.mind.vampire.force_add_ability(/datum/vampire_passive/eternal_darkness)
if(!V.get_ability(/datum/vampire_passive/eternal_darkness))
V.force_add_ability(/datum/vampire_passive/eternal_darkness)
target.set_light(6, shroud_power, "#AAD84B")
else
for(var/datum/vampire_passive/eternal_darkness/E in target.mind.vampire.powers)
target.mind.vampire.remove_ability(E)
for(var/datum/vampire_passive/eternal_darkness/E in V.powers)
V.remove_ability(E)
/datum/vampire_passive/eternal_darkness
gain_desc = "You surround yourself in a unnatural darkness, freezing those around you."
@@ -180,14 +182,16 @@
return ..()
/datum/vampire_passive/eternal_darkness/process()
var/datum/antagonist/vampire/V = owner.mind.has_antag_datum(/datum/antagonist/vampire)
for(var/mob/living/L in view(6, owner))
if(L.affects_vampire(owner))
L.adjust_bodytemperature(-20 * TEMPERATURE_DAMAGE_COEFFICIENT)
owner.mind.vampire.bloodusable = max(owner.mind.vampire.bloodusable - 5, 0)
V.bloodusable = max(V.bloodusable - 5, 0)
if(!owner.mind.vampire.bloodusable || owner.stat == DEAD)
owner.mind.vampire.remove_ability(src)
if(!V.bloodusable || owner.stat == DEAD)
V.remove_ability(src)
/datum/vampire_passive/xray
gain_desc = "You can now see through walls, incase you hadn't noticed."
@@ -1,10 +1,11 @@
//This should hold all the vampire related powers
/mob/living/proc/affects_vampire(mob/user)
//Other vampires aren't affected
if(mind?.vampire)
if(mind?.has_antag_datum(/datum/antagonist/vampire))
return FALSE
//Vampires who have reached their full potential can affect nearly everything
if(user?.mind.vampire.get_ability(/datum/vampire_passive/full))
var/datum/antagonist/vampire/V = user?.mind.has_antag_datum(/datum/antagonist/vampire)
if(V?.get_ability(/datum/vampire_passive/full))
return TRUE
//Holy characters are resistant to vampire powers
if(mind?.isholy)
@@ -21,15 +22,12 @@
var/required_blood
var/deduct_blood_on_cast = TRUE
/obj/effect/proc_holder/spell/vampire/create_new_handler()
var/datum/spell_handler/vampire/H = new
H.required_blood = required_blood
H.deduct_blood_on_cast = deduct_blood_on_cast
return H
/obj/effect/proc_holder/spell/vampire/self
/obj/effect/proc_holder/spell/vampire/self/create_new_targeting()
return new /datum/spell_targeting/self
@@ -46,12 +44,11 @@
owner = null
return ..()
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
name = "Rejuvenate"
desc = "Use reserve blood to enliven your body, removing any incapacitating effects."
action_icon_state = "vampire_rejuvinate"
charge_max = 200
charge_max = 20 SECONDS
stat_allowed = 1
/obj/effect/proc_holder/spell/vampire/self/rejuvenate/cast(list/targets, mob/user = usr)
@@ -64,7 +61,8 @@
U.SetConfused(0)
U.adjustStaminaLoss(-100)
to_chat(user, "<span class='notice'>You instill your body with clean blood and remove any incapacitating effects.</span>")
var/rejuv_bonus = U.mind.vampire.get_rejuv_bonus()
var/datum/antagonist/vampire/V = U.mind.has_antag_datum(/datum/antagonist/vampire)
var/rejuv_bonus = V.get_rejuv_bonus()
if(rejuv_bonus)
INVOKE_ASYNC(src, .proc/heal, U, rejuv_bonus)
@@ -79,13 +77,13 @@
user.reagents.remove_reagent(R.id, 2 * rejuv_bonus)
sleep(35)
/datum/vampire/proc/get_rejuv_bonus()
/datum/antagonist/vampire/proc/get_rejuv_bonus()
var/rejuv_multiplier = 0
if(!get_ability(/datum/vampire_passive/regen))
return rejuv_multiplier
if(subclass?.improved_rejuv_healing)
rejuv_multiplier = clamp((100 - owner.health) / 20, 1, 5) // brute and burn healing between 5 and 50
rejuv_multiplier = clamp((100 - owner.current.health) / 20, 1, 5) // brute and burn healing between 5 and 50
return rejuv_multiplier
return 1
@@ -109,14 +107,14 @@
ui.open()
/obj/effect/proc_holder/spell/vampire/self/specialize/ui_data(mob/user)
var/datum/vampire/vamp = user.mind.vampire
var/datum/antagonist/vampire/vamp = user.mind.has_antag_datum(/datum/antagonist/vampire)
var/list/data = list("subclasses" = vamp.subclass)
return data
/obj/effect/proc_holder/spell/vampire/self/specialize/ui_act(action, list/params)
if(..())
return
var/datum/vampire/vamp = usr.mind.vampire
var/datum/antagonist/vampire/vamp = usr.mind.has_antag_datum(/datum/antagonist/vampire)
if(vamp.subclass)
vamp.upgrade_tiers -= type
@@ -138,7 +136,7 @@
vamp.remove_ability(src)
/datum/vampire/proc/add_subclass(subclass_to_add, announce = TRUE)
/datum/antagonist/vampire/proc/add_subclass(subclass_to_add, announce = TRUE)
var/datum/vampire_subclass/new_subclass = new subclass_to_add
subclass = new_subclass
check_vampire_upgrade(announce)
@@ -281,7 +279,7 @@
if(H.dna && (NO_BLOOD in H.dna.species.species_traits) || H.dna.species.exotic_blood || !H.blood_volume)
visible_message("[H] looks unfazed!")
return
if(H.mind.vampire || H.mind.special_role == SPECIAL_ROLE_VAMPIRE || H.mind.special_role == SPECIAL_ROLE_VAMPIRE_THRALL)
if(H.mind.has_antag_datum(/datum/antagonist/vampire) || H.mind.special_role == SPECIAL_ROLE_VAMPIRE || H.mind.special_role == SPECIAL_ROLE_VAMPIRE_THRALL)
visible_message("<span class='notice'>[H] looks refreshed!</span>")
H.adjustBruteLoss(-60)
H.adjustFireLoss(-60)
@@ -321,7 +319,7 @@
desc = "Teleport to a nearby dark region"
gain_desc = "You have gained the ability to shadowstep, which makes you disappear into nearby shadows at the cost of blood."
action_icon_state = "shadowblink"
charge_max = 20
charge_max = 2 SECONDS
clothes_req = FALSE
centcom_cancast = FALSE
include_space = FALSE
@@ -345,7 +343,7 @@
// pure adminbus at the moment
/proc/isvampirethrall(mob/living/M)
return istype(M) && M.mind && SSticker.mode && (M.mind in SSticker.mode.vampire_enthralled)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/mindslave/thrall)
/obj/effect/proc_holder/spell/vampire/enthrall
name = "Enthrall (150)"
@@ -365,7 +363,7 @@
return T
/obj/effect/proc_holder/spell/vampire/enthrall/cast(list/targets, mob/user = usr)
var/datum/vampire/vampire = user.mind.vampire
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
for(var/mob/living/target in targets)
user.visible_message("<span class='warning'>[user] bites [target]'s neck!</span>", "<span class='warning'>You bite [target]'s neck and begin the flow of power.</span>")
to_chat(target, "<span class='warning'>You feel the tendrils of evil invade your mind.</span>")
@@ -395,7 +393,7 @@
if(!C.mind)
to_chat(user, "<span class='warning'>[C.name]'s mind is not there for you to enthrall.</span>")
return FALSE
if(enthrall_safe || (C.mind in SSticker.mode.vampires) || (C.mind.vampire) || (C.mind in SSticker.mode.vampire_enthralled))
if(enthrall_safe || (C.mind.has_antag_datum(/datum/antagonist/vampire)) || (isvampirethrall(C)))
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>You feel a familiar sensation in your skull that quickly dissipates.</span>")
return FALSE
if(!C.affects_vampire(user))
@@ -411,34 +409,9 @@
/obj/effect/proc_holder/spell/vampire/enthrall/proc/handle_enthrall(mob/living/user, mob/living/carbon/human/H)
if(!istype(H))
return 0
var/ref = "\ref[user.mind]"
if(!(ref in SSticker.mode.vampire_thralls))
SSticker.mode.vampire_thralls[ref] = list(H.mind)
else
SSticker.mode.vampire_thralls[ref] += H.mind
SSticker.mode.update_vampire_icons_added(H.mind)
SSticker.mode.update_vampire_icons_added(user.mind)
var/datum/mindslaves/slaved = user.mind.som
if(!slaved)
slaved = new()
slaved.masters = user.mind
H.mind.som = slaved
slaved.serv += H
slaved.add_serv_hud(user.mind, "vampire")//handles master servent icons
slaved.add_serv_hud(H.mind, "vampthrall")
return FALSE
SSticker.mode.vampire_enthralled.Add(H.mind)
SSticker.mode.vampire_enthralled[H.mind] = user.mind
H.mind.special_role = SPECIAL_ROLE_VAMPIRE_THRALL
var/datum/objective/protect/serve_objective = new
serve_objective.owner = user.mind
serve_objective.target = H.mind
serve_objective.explanation_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.objectives += serve_objective
to_chat(H, "<span class='biggerdanger'>You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.</span>")
to_chat(user, "<span class='warning'>You have successfully Enthralled [H]. <i>If [H.p_they()] refuse[H.p_s()] to do as you say just adminhelp.</i></span>")
var/greet_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.add_antag_datum(new /datum/antagonist/mindslave/thrall(user.mind, greet_text))
H.Stun(2)
add_attack_logs(user, H, "Vampire-thralled")
@@ -8,12 +8,12 @@
/// Whether or not a vampire heals more based on damage taken.
var/improved_rejuv_healing = FALSE
/datum/vampire_subclass/proc/add_subclass_ability(datum/vampire/vamp)
/datum/vampire_subclass/proc/add_subclass_ability(datum/antagonist/vampire/vamp)
for(var/thing in standard_powers)
if(vamp.bloodtotal >= standard_powers[thing])
vamp.add_ability(thing)
/datum/vampire_subclass/proc/add_full_power_abilities(datum/vampire/vamp)
/datum/vampire_subclass/proc/add_full_power_abilities(datum/antagonist/vampire/vamp)
for(var/thing in fully_powered_abilities)
vamp.add_ability(thing)
+2 -2
View File
@@ -272,7 +272,7 @@
if(user.mind.changeling) //Changelings
to_chat(user, "<span class ='warning'>We try multiple times, but we are not able to comprehend the contents of the scroll!</span>")
return
else if(user.mind.vampire) //Vampires
else if(user.mind.has_antag_datum(/datum/antagonist/vampire)) //Vampires
to_chat(user, "<span class ='warning'>Your blood lust distracts you too much to be able to concentrate on the contents of the scroll!</span>")
return
@@ -296,7 +296,7 @@
if(user.mind.changeling) //Changelings
to_chat(user, "<span class='warning'>We try multiple times, but we simply cannot grasp the basics of CQC!</span>")
return
else if(user.mind.vampire) //Vampires
else if(user.mind.has_antag_datum(/datum/antagonist/vampire)) //Vampires
to_chat(user, "<span class='warning'>Your blood lust distracts you from the basics of CQC!</span>")
return
else if(HAS_TRAIT(user, TRAIT_PACIFISM))
+10 -6
View File
@@ -76,11 +76,16 @@
var/datum/mind/mind = M.mind
if(user.antagHUD)
// If a mind is many antags at once, we'll display all of them, each
// under their own antag sub-section.
// This is arguably better, than picking one of the antag datums at random.
/*
If a mind is many antags at once, we'll display all of them, each
under their own antag sub-section.
This is arguably better, than picking one of the antag datums at random.
// Traitors - the only antags in `.antag_datums` at the time of writing.
list of antags that are datumised:
- traitor
- mindslaves/vampire thralls
- vampire
*/
for(var/_A in mind.antag_datums)
var/datum/antagonist/A = _A
var/antag_serialized = serialized.Copy()
@@ -90,8 +95,7 @@
// Not-very-datumized antags follow
// Associative list of antag name => whether this mind is this antag
var/other_antags = list(
"Changeling" = (mind.changeling != null),
"Vampire" = (mind.vampire != null),
"Changeling" = (mind.changeling != null)
)
if(SSticker && SSticker.mode)
other_antags += list(
@@ -207,9 +207,10 @@
stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]")
stat("Absorbed DNA", mind.changeling.absorbedcount)
if(mind.vampire)
stat("Total Blood", "[mind.vampire.bloodtotal]")
stat("Usable Blood", "[mind.vampire.bloodusable]")
var/datum/antagonist/vampire/V = mind.has_antag_datum(/datum/antagonist/vampire)
if(V)
stat("Total Blood", "[V.bloodtotal]")
stat("Usable Blood", "[V.bloodusable]")
/mob/living/carbon/human/ex_act(severity)
if(status_flags & GODMODE)
+7 -5
View File
@@ -36,8 +36,9 @@
name = get_visible_name()
pulse = handle_pulse(times_fired)
if(mind?.vampire)
mind.vampire.handle_vampire()
var/datum/antagonist/vampire/V = mind?.has_antag_datum(/datum/antagonist/vampire)
if(V)
V.handle_vampire()
if(life_tick == 1)
regenerate_icons() // Make sure the inventory updates
@@ -363,8 +364,9 @@
bodytemperature += 11
else
bodytemperature += (BODYTEMP_HEATING_MAX + (fire_stacks * 12))
if(mind?.vampire && !mind.vampire.get_ability(/datum/vampire_passive/full) && stat != DEAD)
mind.vampire.bloodusable = max(mind.vampire.bloodusable - 5, 0)
var/datum/antagonist/vampire/V = mind?.has_antag_datum(/datum/antagonist/vampire)
if(V && !V.get_ability(/datum/vampire_passive/full) && stat != DEAD) // V?. doesn't work here because `has_antag_datum` return FALSE if there is no datum and ?. doesn't like that
V.bloodusable = max(V.bloodusable - 5, 0)
/mob/living/carbon/human/proc/get_thermal_protection()
if(HAS_TRAIT(src, TRAIT_RESISTHEAT))
@@ -788,7 +790,7 @@
/mob/living/carbon/human/proc/handle_nutrition_alerts() //This is a terrible abuse of the alert system; something like this should be a HUD element
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
return
if(mind?.vampire && (mind in SSticker.mode.vampires)) //Vampires
if(mind?.has_antag_datum(/datum/antagonist/vampire)) //Vampires
switch(nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
throw_alert("nutrition", /obj/screen/alert/fat/vampire)
@@ -434,18 +434,19 @@
to_chat(user, "<span class='warning'>You don't want to harm [target]!</span>")
return FALSE
//Vampire code
if(user.mind && user.mind.vampire && (user.mind in SSticker.mode.vampires) && !user.mind.vampire.draining && user.zone_selected == "head" && target != user)
var/datum/antagonist/vampire/V = user?.mind?.has_antag_datum(/datum/antagonist/vampire)
if(V && !V.draining && user.zone_selected == "head" && target != user)
if((NO_BLOOD in target.dna.species.species_traits) || target.dna.species.exotic_blood || !target.blood_volume)
to_chat(user, "<span class='warning'>They have no blood!</span>")
return
if(target.mind && target.mind.vampire && (target.mind in SSticker.mode.vampires))
if(target.mind && target.mind.has_antag_datum(/datum/antagonist/vampire))
to_chat(user, "<span class='warning'>Your fangs fail to pierce [target.name]'s cold flesh</span>")
return
if(HAS_TRAIT(target, TRAIT_SKELETONIZED))
to_chat(user, "<span class='warning'>There is no blood in a skeleton!</span>")
return
//we're good to suck the blood, blaah
user.mind.vampire.handle_bloodsucking(target)
V.handle_bloodsucking(target)
add_attack_logs(user, target, "vampirebit")
return
//end vampire codes
@@ -856,16 +857,17 @@ It'll return null if the organ doesn't correspond, so include null checks when u
if(A.update_remote_sight(H)) //returns 1 if we override all other sight updates.
return
if(H.mind?.vampire)
if(H.mind.vampire.get_ability(/datum/vampire_passive/xray))
var/datum/antagonist/vampire/V = H.mind?.has_antag_datum(/datum/antagonist/vampire)
if(V)
if(V.get_ability(/datum/vampire_passive/xray))
H.sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
H.see_in_dark += 8
H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
else if(H.mind.vampire.get_ability(/datum/vampire_passive/full))
else if(V.get_ability(/datum/vampire_passive/full))
H.sight |= SEE_MOBS
H.see_in_dark += 8
H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
else if(H.mind.vampire.get_ability(/datum/vampire_passive/vision))
else if(V.get_ability(/datum/vampire_passive/vision))
H.sight |= SEE_MOBS
H.see_in_dark += 1 // base of 2, 2+1 is 3
H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
@@ -85,7 +85,7 @@
/datum/species/diona/handle_life(mob/living/carbon/human/H)
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
var/is_vamp = H.mind?.vampire != null
var/is_vamp = H.mind && H.mind.has_antag_datum(/datum/antagonist/vampire)
if(isturf(H.loc)) //else, there's considered to be no light
var/turf/T = H.loc
light_amount = min(1, T.get_lumcount()) - 0.5
+1 -1
View File
@@ -322,7 +322,7 @@
/mob/living/carbon/handle_sleeping()
if(..())
if(mind?.vampire)
if(mind?.has_antag_datum(/datum/antagonist/vampire))
if(istype(loc, /obj/structure/closet/coffin))
adjustBruteLoss(-1, FALSE)
adjustFireLoss(-1, FALSE)
+1 -1
View File
@@ -5,7 +5,7 @@
SSticker.mode.remove_cultist(mind, 1)
SSticker.mode.remove_wizard(mind)
SSticker.mode.remove_changeling(mind)
SSticker.mode.remove_vampire(mind)
mind.remove_antag_datum(/datum/antagonist/vampire)
SSticker.mode.remove_thrall(mind, 0)
SSticker.mode.remove_shadowling(mind)
SSticker.mode.remove_abductor(mind)
-5
View File
@@ -1499,11 +1499,6 @@ GLOBAL_LIST_INIT(holy_areas, typecacheof(list(
if(!mind)
return FALSE
//Allows fullpower vampires to bypass holy areas
var/datum/vampire/vampire = mind.vampire
if(vampire && vampire.get_ability(/datum/vampire_passive/full))
return FALSE
//Allows cult to bypass holy areas once they summon
var/datum/game_mode/gamemode = SSticker.mode
if(iscultist(src) && gamemode.cult_objs.cult_status == NARSIE_HAS_RISEN)
@@ -12,11 +12,11 @@
var/diet_flags = DIET_OMNI | DIET_HERB | DIET_CARN
/datum/reagent/consumable/on_mob_life(mob/living/M)
if(!(M.mind in SSticker.mode.vampires))
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.can_eat(diet_flags)) //Make sure the species has it's dietflag set, otherwise it can't digest any nutrients
H.adjust_nutrition(nutriment_factor) // For hunger and fatness
var/is_vamp = M.mind?.has_antag_datum(/datum/antagonist/vampire)
if(ishuman(M) && !is_vamp)
var/mob/living/carbon/human/H = M
if(H.can_eat(diet_flags)) //Make sure the species has it's dietflag set, otherwise it can't digest any nutrients
H.adjust_nutrition(nutriment_factor) // For hunger and fatness
return ..()
/datum/reagent/consumable/nutriment // Pure nutriment, universally digestable and thus slightly less effective
@@ -31,13 +31,13 @@
/datum/reagent/consumable/nutriment/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
if(!(M.mind in SSticker.mode.vampires))
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.can_eat(diet_flags)) //Make sure the species has it's dietflag set, otherwise it can't digest any nutrients
if(prob(50))
update_flags |= M.adjustBruteLoss(-brute_heal, FALSE)
update_flags |= M.adjustFireLoss(-burn_heal, FALSE)
var/is_vamp = M.mind?.has_antag_datum(/datum/antagonist/vampire)
if(ishuman(M) && !is_vamp)
var/mob/living/carbon/human/H = M
if(H.can_eat(diet_flags)) //Make sure the species has it's dietflag set, otherwise it can't digest any nutrients
if(prob(50))
update_flags |= M.adjustBruteLoss(-brute_heal, FALSE)
update_flags |= M.adjustFireLoss(-burn_heal, FALSE)
return ..() | update_flags
/datum/reagent/consumable/nutriment/on_new(list/supplied_data)
@@ -354,7 +354,8 @@
var/update_flags = STATUS_UPDATE_NONE
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.mind && H.mind.vampire && !H.mind.vampire.get_ability(/datum/vampire_passive/full)) //incapacitating but not lethal.
var/datum/antagonist/vampire/V = H.mind?.has_antag_datum(/datum/antagonist/vampire)
if(V && !V.get_ability(/datum/vampire_passive/full)) //incapacitating but not lethal.
if(prob(min(25, current_cycle)))
to_chat(H, "<span class='danger'>You can't get the scent of garlic out of your nose! You can barely think...</span>")
H.Weaken(1)
@@ -107,7 +107,7 @@
C.reagents.add_reagent("toxin", volume * 0.5)
else
C.blood_volume = min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL)
if(C.mind?.vampire)
if(C.mind?.has_antag_datum(/datum/antagonist/vampire))
C.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, C.nutrition + 10))
/datum/reagent/blood/on_new(list/data)
@@ -249,7 +249,7 @@
if(current_cycle >= 75 && prob(33)) // 30 units, 150 seconds
M.AdjustConfused(3)
if(isvampirethrall(M))
SSticker.mode.remove_vampire_mind(M.mind)
M.mind.remove_antag_datum(/datum/antagonist/mindslave/thrall)
holder.remove_reagent(id, volume)
M.SetJitter(0)
M.SetStuttering(0)
@@ -262,17 +262,18 @@
M.SetStuttering(0)
M.SetConfused(0)
return
if(ishuman(M) && M.mind && M.mind.vampire && !M.mind.vampire.get_ability(/datum/vampire_passive/full) && prob(80))
var/datum/antagonist/vampire/vamp = M.mind?.has_antag_datum(/datum/antagonist/vampire)
if(ishuman(M) && vamp && !vamp.get_ability(/datum/vampire_passive/full) && prob(80))
var/mob/living/carbon/V = M
if(M.mind.vampire.bloodusable)
if(vamp.bloodusable)
M.Stuttering(1)
M.Jitter(30)
update_flags |= M.adjustStaminaLoss(5, FALSE)
if(prob(20))
M.emote("scream")
M.mind.vampire.adjust_nullification(20, 4)
M.mind.vampire.bloodusable = max(M.mind.vampire.bloodusable - 3,0)
if(M.mind.vampire.bloodusable)
vamp.adjust_nullification(20, 4)
vamp.bloodusable = max(vamp.bloodusable - 3,0)
if(vamp.bloodusable)
V.vomit(0, TRUE, FALSE)
V.adjustBruteLoss(3)
else
@@ -283,7 +284,7 @@
switch(current_cycle)
if(1 to 4)
to_chat(M, "<span class = 'warning'>Something sizzles in your veins!</span>")
M.mind.vampire.adjust_nullification(20, 4)
vamp.adjust_nullification(20, 4)
if(5 to 12)
to_chat(M, "<span class = 'danger'>You feel an intense burning inside of you!</span>")
update_flags |= M.adjustFireLoss(1, FALSE)
@@ -291,7 +292,7 @@
M.Jitter(20)
if(prob(20))
M.emote("scream")
M.mind.vampire.adjust_nullification(20, 4)
vamp.adjust_nullification(20, 4)
if(13 to INFINITY)
M.visible_message("<span class='danger'>[M] suddenly bursts into flames!</span>",
"<span class='danger'>You suddenly ignite in a holy fire!</span>")
@@ -302,14 +303,15 @@
M.Jitter(30)
if(prob(40))
M.emote("scream")
M.mind.vampire.adjust_nullification(20, 4)
vamp.adjust_nullification(20, 4)
return ..() | update_flags
/datum/reagent/holywater/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)
// Vampires have their powers weakened by holy water applied to the skin.
if(ishuman(M) && M.mind && M.mind.vampire && !M.mind.vampire.get_ability(/datum/vampire_passive/full))
var/mob/living/carbon/human/H=M
var/datum/antagonist/vampire/V = M.mind?.has_antag_datum(/datum/antagonist/vampire)
if(ishuman(M) && V && !V.get_ability(/datum/vampire_passive/full))
var/mob/living/carbon/human/H = M
if(method == REAGENT_TOUCH)
if(H.wear_mask)
to_chat(H, "<span class='warning'>Your mask protects you from the holy water!</span>")
@@ -319,7 +321,7 @@
return
else
to_chat(M, "<span class='warning'>Something holy interferes with your powers!</span>")
M.mind.vampire.adjust_nullification(5, 2)
V.adjust_nullification(5, 2)
/datum/reagent/holywater/reaction_turf(turf/simulated/T, volume)
+7 -6
View File
@@ -635,12 +635,7 @@
#include "code\game\gamemodes\shadowling\special_shadowling_abilities.dm"
#include "code\game\gamemodes\traitor\traitor.dm"
#include "code\game\gamemodes\vampire\traitor_vamp.dm"
#include "code\game\gamemodes\vampire\vampire.dm"
#include "code\game\gamemodes\vampire\vampire_subclasses.dm"
#include "code\game\gamemodes\vampire\vampire_powers\gargantua_powers.dm"
#include "code\game\gamemodes\vampire\vampire_powers\hemomancer_powers.dm"
#include "code\game\gamemodes\vampire\vampire_powers\umbrae_powers.dm"
#include "code\game\gamemodes\vampire\vampire_powers\vampire_powers.dm"
#include "code\game\gamemodes\vampire\vampire_gamemode.dm"
#include "code\game\gamemodes\wizard\artefact.dm"
#include "code\game\gamemodes\wizard\godhand.dm"
#include "code\game\gamemodes\wizard\raginmages.dm"
@@ -1293,6 +1288,12 @@
#include "code\modules\antagonists\traitor\contractor\items\contractor_pinpointer.dm"
#include "code\modules\antagonists\traitor\contractor\items\contractor_uplink.dm"
#include "code\modules\antagonists\traitor\contractor\items\extraction_items.dm"
#include "code\modules\antagonists\vampire\vamp_datum.dm"
#include "code\modules\antagonists\vampire\vampire_subclasses.dm"
#include "code\modules\antagonists\vampire\vampire_powers\gargantua_powers.dm"
#include "code\modules\antagonists\vampire\vampire_powers\hemomancer_powers.dm"
#include "code\modules\antagonists\vampire\vampire_powers\umbrae_powers.dm"
#include "code\modules\antagonists\vampire\vampire_powers\vampire_powers.dm"
#include "code\modules\antagonists\wishgranter\wishgranter.dm"
#include "code\modules\arcade\arcade_base.dm"
#include "code\modules\arcade\arcade_prize.dm"