mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 10:33:30 +01:00
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:
+16
-37
@@ -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))
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
-1
@@ -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))
|
||||
+15
-14
@@ -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)
|
||||
+18
-14
@@ -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."
|
||||
+19
-46
@@ -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")
|
||||
+2
-2
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user