mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-24 04:27:35 +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:
co-authored by
Farie82
dearmochi
parent
b2704e06b2
commit
a0e5d6df44
@@ -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
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_swell
|
||||
name = "Blood Swell (30)"
|
||||
desc = "You infuse your body with blood, making you highly resistant to stuns and physical damage. However, this makes you unable to fire ranged weapons while it is active."
|
||||
gain_desc = "You have gained the ability to temporarly resist large amounts of stuns and physical damage."
|
||||
charge_max = 40 SECONDS
|
||||
required_blood = 30
|
||||
action_icon_state = "blood_swell"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_swell/cast(list/targets, mob/user)
|
||||
var/mob/living/target = targets[1]
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
H.apply_status_effect(STATUS_EFFECT_BLOOD_SWELL)
|
||||
|
||||
/datum/vampire_passive/blood_swell_upgrade
|
||||
gain_desc = "While blood swell is active all of your melee attacks deal increased damage."
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/overwhelming_force
|
||||
name = "Overwhelming Force"
|
||||
desc = "When toggled you will automatically pry open doors that you bump into if you do not have access."
|
||||
gain_desc = "You have gained the ability to force open doors at a small blood cost."
|
||||
charge_max = 2 SECONDS
|
||||
action_icon_state = "OH_YEAAAAH"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/overwhelming_force/cast(list/targets, mob/user)
|
||||
if(!HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT))
|
||||
to_chat(user, "<span class='warning'>You feel MIGHTY!</span>")
|
||||
ADD_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)
|
||||
user.status_flags &= ~CANPUSH
|
||||
user.move_resist = MOVE_FORCE_STRONG
|
||||
else
|
||||
REMOVE_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)
|
||||
user.move_resist = MOVE_FORCE_DEFAULT
|
||||
user.status_flags |= CANPUSH
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_rush
|
||||
name = "Blood Rush (30)"
|
||||
desc = "Infuse yourself with blood magic to boost your movement speed."
|
||||
gain_desc = "You have gained the ability to temporarily move at high speeds."
|
||||
charge_max = 30 SECONDS
|
||||
required_blood = 30
|
||||
action_icon_state = "blood_rush"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_rush/cast(list/targets, mob/user)
|
||||
var/mob/living/target = targets[1]
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
to_chat(H, "<span class='notice'>You feel a rush of energy!</span>")
|
||||
H.apply_status_effect(STATUS_EFFECT_BLOOD_RUSH)
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/charge
|
||||
name = "Charge (30)"
|
||||
desc = "You charge at wherever you click on screen, dealing large amounts of damage, stunning and destroying walls and other objects."
|
||||
gain_desc = "You can now charge at a target on screen, dealing massive damage and destroying structures."
|
||||
required_blood = 30
|
||||
charge_max = 30 SECONDS
|
||||
action_icon_state = "vampire_charge"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/charge/create_new_targeting()
|
||||
return new /datum/spell_targeting/clicked_atom
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/charge/can_cast(mob/user, charge_check, show_message)
|
||||
var/mob/living/L = user
|
||||
if(L.IsWeakened() || L.resting)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/charge/cast(list/targets, mob/user)
|
||||
var/target = targets[1]
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
L.apply_status_effect(STATUS_EFFECT_CHARGING)
|
||||
L.throw_at(target, targeting.range, 1, L, FALSE, callback = CALLBACK(L, /mob/living/.proc/remove_status_effect, STATUS_EFFECT_CHARGING))
|
||||
@@ -1,245 +0,0 @@
|
||||
/obj/effect/proc_holder/spell/vampire/self/vamp_claws
|
||||
name = "Vampiric Claws (30)"
|
||||
desc = "You channel blood magics to forge deadly vampiric claws that leech blood and strike rapidly. Cannot be used if you are holding something that cannot be dropped."
|
||||
gain_desc = "You have gained the ability to forge your hands into vampiric claws."
|
||||
charge_max = 30 SECONDS
|
||||
required_blood = 30
|
||||
action_icon_state = "vampire_claws"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/vamp_claws/cast(mob/user)
|
||||
if(user.l_hand || user.r_hand)
|
||||
to_chat(user, "<span class='notice'>You drop what was in your hands as large blades spring from your fingers!</span>")
|
||||
user.drop_l_hand()
|
||||
user.drop_r_hand()
|
||||
else
|
||||
to_chat(user, "<span class='notice'>Large blades of blood spring from your fingers!</span>")
|
||||
var/obj/item/twohanded/required/vamp_claws/claws = new /obj/item/twohanded/required/vamp_claws(user.loc)
|
||||
user.put_in_hands(claws)
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/vamp_claws/can_cast(mob/user, charge_check, show_message)
|
||||
var/mob/living/L = user
|
||||
if(L.canUnEquip(L.l_hand) && L.canUnEquip(L.r_hand))
|
||||
return ..()
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws
|
||||
name = "vampiric claws"
|
||||
desc = "A pair of eldritch claws made of living blood, they seem to flow yet they are solid"
|
||||
icon = 'icons/effects/vampire_effects.dmi'
|
||||
icon_state = "vamp_claws"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
flags = ABSTRACT | NODROP | DROPDEL
|
||||
force = 10
|
||||
force_wielded = 10
|
||||
armour_penetration = 20
|
||||
block_chance = 50
|
||||
sharp = TRUE
|
||||
attack_effect_override = ATTACK_EFFECT_CLAW
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut", "savaged", "clawed")
|
||||
sprite_sheets_inhand = list("Vox" = 'icons/mob/clothing/species/vox/held.dmi', "Drask" = 'icons/mob/clothing/species/drask/held.dmi')
|
||||
var/durability = 20
|
||||
var/blood_drain_amount = 15
|
||||
var/blood_absorbed_amount = 5
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/afterattack(atom/target, mob/user, proximity)
|
||||
if(!user.mind?.vampire)
|
||||
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)
|
||||
durability -= 1
|
||||
if(durability <= 0)
|
||||
qdel(src)
|
||||
to_chat(user, "<span class='warning'>Your claws shatter!</span>")
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/process()
|
||||
durability -= 1
|
||||
if(durability <= 0)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
|
||||
if(attack_type == PROJECTILE_ATTACK)
|
||||
final_block_chance = 0
|
||||
return ..()
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/melee_attack_chain(mob/user, atom/target, params)
|
||||
..()
|
||||
if(wielded)
|
||||
user.changeNext_move(CLICK_CD_MELEE * 0.5)
|
||||
|
||||
/obj/item/twohanded/required/vamp_claws/attack_self(mob/user)
|
||||
to_chat(user, "<span class='notice'>You dispel your claws!</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/blood_tendrils
|
||||
name = "Blood Tendrils (10)"
|
||||
desc = "You summon blood tendrils from bluespace after a delay to ensnare people in an area, slowing them down."
|
||||
gain_desc = "You have gained the ability to summon blood tendrils to slow people down in an area that you target."
|
||||
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
|
||||
|
||||
selection_activated_message = "<span class='notice'>You channel blood magics to weaken the bluespace veil. <B>Left-click to cast at a target area!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your magics subside.</span>"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/blood_tendrils/create_new_targeting()
|
||||
var/datum/spell_targeting/click/T = new
|
||||
T.allowed_type = /atom
|
||||
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
|
||||
|
||||
for(var/turf/simulated/blood_turf in view(area_of_affect, T))
|
||||
if(blood_turf.density)
|
||||
continue
|
||||
new /obj/effect/temp_visual/blood_tendril(blood_turf)
|
||||
|
||||
addtimer(CALLBACK(src, .proc/apply_slowdown, T, area_of_affect, 3, user), 0.5 SECONDS)
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/blood_tendrils/proc/apply_slowdown(turf/T, distance, slowed_amount, mob/user)
|
||||
for(var/mob/living/L in range(distance, T))
|
||||
if(L.affects_vampire(user))
|
||||
L.AdjustSlowed(slowed_amount)
|
||||
L.visible_message("<span class='warning'>[L] gets ensared in blood tendrils, restricting [L.p_their()] movement!</span>")
|
||||
new /obj/effect/temp_visual/blood_tendril/long(get_turf(L))
|
||||
|
||||
/obj/effect/temp_visual/blood_tendril
|
||||
icon = 'icons/effects/vampire_effects.dmi'
|
||||
icon_state = "blood_tendril"
|
||||
duration = 1 SECONDS
|
||||
|
||||
/obj/effect/temp_visual/blood_tendril/long
|
||||
duration = 2 SECONDS
|
||||
|
||||
/obj/effect/proc_holder/spell/ethereal_jaunt/blood_pool
|
||||
name = "Sanguine Pool (50)"
|
||||
desc = "You shift your form into a pool of blood, making you invulnerable and able to move through anything that's not a wall or space. You leave a trail of blood behind you when you do this."
|
||||
gain_desc = "You have gained the ability to shift into a pool of blood, allowing you to evade pursuers with great mobility."
|
||||
jaunt_duration = 3 SECONDS
|
||||
clothes_req = FALSE
|
||||
panel = "Vampire"
|
||||
school = "vampire"
|
||||
action_background_icon_state = "bg_vampire"
|
||||
action_icon_state = "blood_pool"
|
||||
jaunt_type_path = /obj/effect/dummy/spell_jaunt/blood_pool
|
||||
jaunt_water_effect = FALSE
|
||||
jaunt_out_type = /obj/effect/temp_visual/dir_setting/cult/phase/out
|
||||
jaunt_in_type = /obj/effect/temp_visual/dir_setting/cult/phase
|
||||
jaunt_in_time = 0
|
||||
sound1 = 'sound/misc/enter_blood.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/ethereal_jaunt/blood_pool/create_new_handler()
|
||||
var/datum/spell_handler/vampire/H = new
|
||||
H.required_blood = 50
|
||||
return H
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/blood_eruption
|
||||
name = "Blood Eruption (100)"
|
||||
desc = "Every pool of blood in 4 tiles erupts with a spike of living blood, damaging anyone stood on it."
|
||||
gain_desc = "You have gained the ability to weaponise pools of blood to damage those stood on them."
|
||||
required_blood = 100
|
||||
charge_max = 200 SECONDS
|
||||
action_icon_state = "blood_spikes"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/blood_eruption/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/T = new
|
||||
T.range = 4
|
||||
T.allowed_type = /mob/living
|
||||
return T
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/blood_eruption/valid_target(mob/living/target, user)
|
||||
var/turf/T = get_turf(target)
|
||||
if(locate(/obj/effect/decal/cleanable/blood) in T)
|
||||
if(target.affects_vampire(user) && !isLivingSSD(target))
|
||||
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)
|
||||
var/obj/effect/decal/cleanable/blood/B = locate(/obj/effect/decal/cleanable/blood) in T
|
||||
var/obj/effect/temp_visual/blood_spike/spike = new /obj/effect/temp_visual/blood_spike(T)
|
||||
spike.color = B.basecolor
|
||||
playsound(L, 'sound/misc/demon_attack1.ogg', 50, TRUE)
|
||||
L.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
|
||||
L.visible_message("<span class='warning'><b>[L] gets impaled by a spike of living blood!</b></span>")
|
||||
|
||||
/obj/effect/temp_visual/blood_spike
|
||||
icon = 'icons/effects/vampire_effects.dmi'
|
||||
icon_state = "bloodspike_white"
|
||||
duration = 0.3 SECONDS
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_spill
|
||||
name = "The Blood Bringers Rite"
|
||||
desc = "When toggled, everyone around you begins to bleed profusely."
|
||||
gain_desc = "You have gained the ability to rip the very life force out of people and absorb it, healing you."
|
||||
charge_max = 10 SECONDS
|
||||
action_icon_state = "blood_bringers_rite"
|
||||
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)
|
||||
else
|
||||
for(var/datum/vampire_passive/blood_spill/B in target.mind.vampire.powers)
|
||||
target.mind.vampire.remove_ability(B)
|
||||
|
||||
/datum/vampire_passive/blood_spill
|
||||
var/max_beams = 10
|
||||
|
||||
/datum/vampire_passive/blood_spill/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/datum/vampire_passive/blood_spill/Destroy(force, ...)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/datum/vampire_passive/blood_spill/process()
|
||||
var/beam_number = 0
|
||||
var/turf/T = get_turf(owner)
|
||||
for(var/mob/living/carbon/human/H in view(7, T))
|
||||
if(NO_BLOOD in H.dna.species.species_traits)
|
||||
continue
|
||||
|
||||
if(!H.affects_vampire(owner) || H.stat)
|
||||
continue
|
||||
|
||||
var/drain_amount = rand(5, 10)
|
||||
beam_number++
|
||||
H.bleed(drain_amount)
|
||||
H.Beam(owner, icon_state = "drainbeam", time = 2 SECONDS)
|
||||
H.adjustBruteLoss(2)
|
||||
owner.heal_overall_damage(8, 2, TRUE)
|
||||
owner.adjustStaminaLoss(-15)
|
||||
owner.AdjustStunned(-1)
|
||||
owner.AdjustWeakened(-1)
|
||||
if(drain_amount == 10)
|
||||
to_chat(H, "<span class='warning'>You feel your life force draining!</b></span>")
|
||||
|
||||
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)
|
||||
@@ -1,193 +0,0 @@
|
||||
/obj/effect/proc_holder/spell/vampire/self/cloak
|
||||
name = "Cloak of Darkness"
|
||||
desc = "Toggles whether you are currently cloaking yourself in darkness. When in darkness and toggled on, you move at increased speeds."
|
||||
gain_desc = "You have gained the Cloak of Darkness ability, which when toggled makes you nearly invisible and highly agile in the shroud of darkness."
|
||||
action_icon_state = "vampire_cloak"
|
||||
charge_max = 2 SECONDS
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/cloak/New()
|
||||
..()
|
||||
update_name()
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/cloak/proc/update_name()
|
||||
var/mob/living/user = loc
|
||||
if(!ishuman(user) || !user.mind || !user.mind.vampire)
|
||||
return
|
||||
action.button.name = "[initial(name)] ([user.mind.vampire.iscloaking ? "Deactivate" : "Activate"])"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/cloak/cast(list/targets, mob/user = usr)
|
||||
var/datum/vampire/V = user.mind.vampire
|
||||
V.iscloaking = !V.iscloaking
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(V.iscloaking)
|
||||
H.physiology.burn_mod *= 1.3
|
||||
user.RegisterSignal(user, COMSIG_LIVING_IGNITED, /mob/living.proc/update_vampire_cloak)
|
||||
else
|
||||
user.UnregisterSignal(user, COMSIG_LIVING_IGNITED)
|
||||
H.physiology.burn_mod /= 1.3
|
||||
|
||||
update_name()
|
||||
to_chat(user, "<span class='notice'>You will now be [V.iscloaking ? "hidden" : "seen"] in darkness.</span>")
|
||||
|
||||
/mob/living/proc/update_vampire_cloak()
|
||||
SIGNAL_HANDLER
|
||||
mind.vampire.handle_vampire_cloak()
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/shadow_snare
|
||||
name = "Shadow Snare (20)"
|
||||
desc = "You summon a trap on the ground. When crossed it will blind the target, extinguish any lights they may have, and ensnare them."
|
||||
gain_desc = "You have gained the ability to summon a trap that will blind, ensnare, and turn off the lights of anyone who crosses it."
|
||||
charge_max = 20 SECONDS
|
||||
required_blood = 20
|
||||
action_icon_state = "shadow_snare"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/shadow_snare/create_new_targeting()
|
||||
var/datum/spell_targeting/click/T = new
|
||||
T.allowed_type = /turf/simulated
|
||||
T.click_radius = -1
|
||||
return T
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/shadow_snare/cast(list/targets, mob/user)
|
||||
var/turf/target = targets[1]
|
||||
new /obj/item/restraints/legcuffs/beartrap/shadow_snare(target)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare
|
||||
name = "shadow snare"
|
||||
desc = "An almost transparent trap that melts into the shadows."
|
||||
alpha = 60
|
||||
armed = TRUE
|
||||
anchored = TRUE
|
||||
breakouttime = 5 SECONDS
|
||||
flags = DROPDEL
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare/Crossed(AM, oldloc)
|
||||
if(!iscarbon(AM) || !armed)
|
||||
return
|
||||
var/mob/living/carbon/C = AM
|
||||
if(!C.affects_vampire()) // no parameter here so holy always protects
|
||||
return
|
||||
C.extinguish_light()
|
||||
C.EyeBlind(10)
|
||||
STOP_PROCESSING(SSobj, src) // won't wither away once you are trapped
|
||||
..()
|
||||
if(!iscarbon(loc)) // if it fails to latch onto someone for whatever reason, delete itself, we don't want unarmed ones lying around.
|
||||
qdel(src)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare/attack_hand(mob/user)
|
||||
Crossed(user)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare/attack_tk(mob/user)
|
||||
if(iscarbon(user))
|
||||
to_chat(user, "<span class='userdanger'>The snare sends a psychic backlash!</span>")
|
||||
user.EyeBlind(10)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare/process()
|
||||
var/turf/T = get_turf(src)
|
||||
var/lighting_count = T.get_lumcount() * 10
|
||||
if(lighting_count > 2)
|
||||
obj_integrity -= 50
|
||||
|
||||
if(obj_integrity <= 0)
|
||||
visible_message("<span class='notice'>[src] withers away.</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/shadow_snare/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/dark_passage
|
||||
name = "Dark Passage (30)"
|
||||
desc = "You teleport to a targeted turf."
|
||||
gain_desc = "You have gained the ability to blink a short distance towards a targeted turf."
|
||||
charge_max = 40 SECONDS
|
||||
required_blood = 30
|
||||
centcom_cancast = FALSE
|
||||
action_icon_state = "dark_passage"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/dark_passage/create_new_targeting()
|
||||
var/datum/spell_targeting/click/T = new
|
||||
T.click_radius = 0
|
||||
T.allowed_type = /turf/simulated
|
||||
return T
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/dark_passage/cast(list/targets, mob/user)
|
||||
var/turf/target = get_turf(targets[1])
|
||||
|
||||
new /obj/effect/temp_visual/vamp_mist_out(get_turf(user))
|
||||
|
||||
user.forceMove(target)
|
||||
|
||||
/obj/effect/temp_visual/vamp_mist_out
|
||||
duration = 2 SECONDS
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "mist"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/vamp_extinguish
|
||||
name = "Extinguish"
|
||||
desc = "You extinguish any light source in an area around you."
|
||||
gain_desc = "You have gained the ability to extinguish nearby light sources."
|
||||
charge_max = 20 SECONDS
|
||||
action_icon_state = "vampire_extinguish"
|
||||
create_attack_logs = FALSE
|
||||
create_custom_logs = TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/vamp_extinguish/create_new_targeting()
|
||||
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()
|
||||
for(var/atom/A in T.contents)
|
||||
A.extinguish_light()
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness
|
||||
name = "Eternal Darkness"
|
||||
desc = "When toggled, you shroud the area around you in darkness and slowly lower the body temperature of people nearby."
|
||||
gain_desc = "You have gained the ability to shroud the area around you in darkness, only the strongest of lights can pierce your unholy powers."
|
||||
charge_max = 10 SECONDS
|
||||
action_icon_state = "eternal_darkness"
|
||||
required_blood = 5
|
||||
var/shroud_power = -4
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness/cast(list/targets, mob/user)
|
||||
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)
|
||||
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)
|
||||
|
||||
/datum/vampire_passive/eternal_darkness
|
||||
gain_desc = "You surround yourself in a unnatural darkness, freezing those around you."
|
||||
|
||||
/datum/vampire_passive/eternal_darkness/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/datum/vampire_passive/eternal_darkness/Destroy(force, ...)
|
||||
owner.remove_light()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/datum/vampire_passive/eternal_darkness/process()
|
||||
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)
|
||||
|
||||
if(!owner.mind.vampire.bloodusable || owner.stat == DEAD)
|
||||
owner.mind.vampire.remove_ability(src)
|
||||
|
||||
/datum/vampire_passive/xray
|
||||
gain_desc = "You can now see through walls, incase you hadn't noticed."
|
||||
@@ -1,444 +0,0 @@
|
||||
//This should hold all the vampire related powers
|
||||
/mob/living/proc/affects_vampire(mob/user)
|
||||
//Other vampires aren't affected
|
||||
if(mind?.vampire)
|
||||
return FALSE
|
||||
//Vampires who have reached their full potential can affect nearly everything
|
||||
if(user?.mind.vampire.get_ability(/datum/vampire_passive/full))
|
||||
return TRUE
|
||||
//Holy characters are resistant to vampire powers
|
||||
if(mind?.isholy)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire
|
||||
panel = "Vampire"
|
||||
school = "vampire"
|
||||
action_background_icon_state = "bg_vampire"
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
/// How much blood this ability costs to use
|
||||
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
|
||||
|
||||
/datum/vampire_passive
|
||||
var/gain_desc
|
||||
var/mob/living/owner = null
|
||||
|
||||
/datum/vampire_passive/New()
|
||||
..()
|
||||
if(!gain_desc)
|
||||
gain_desc = "You can now use [src]."
|
||||
|
||||
/datum/vampire_passive/Destroy(force, ...)
|
||||
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
|
||||
stat_allowed = 1
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/rejuvenate/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/U = user
|
||||
|
||||
U.SetWeakened(0)
|
||||
U.SetStunned(0)
|
||||
U.SetParalysis(0)
|
||||
U.SetSleeping(0)
|
||||
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()
|
||||
if(rejuv_bonus)
|
||||
INVOKE_ASYNC(src, .proc/heal, U, rejuv_bonus)
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/rejuvenate/proc/heal(mob/living/user, rejuv_bonus)
|
||||
for(var/i in 1 to 5)
|
||||
user.adjustBruteLoss(-2 * rejuv_bonus)
|
||||
user.adjustOxyLoss(-5 * rejuv_bonus)
|
||||
user.adjustToxLoss(-2 * rejuv_bonus)
|
||||
user.adjustFireLoss(-2 * rejuv_bonus)
|
||||
for(var/datum/reagent/R in user.reagents.reagent_list)
|
||||
if(!R.harmless)
|
||||
user.reagents.remove_reagent(R.id, 2 * rejuv_bonus)
|
||||
sleep(35)
|
||||
|
||||
/datum/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
|
||||
return rejuv_multiplier
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/specialize
|
||||
name = "Choose Specialization"
|
||||
desc = "Choose what sub-class of vampire you want to evolve into."
|
||||
gain_desc = "You can now choose what specialization of vampire you want to evolve into."
|
||||
charge_max = 2 SECONDS
|
||||
action_icon_state = "select_class"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/specialize/cast(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/specialize/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "SpecMenu", "Specialisation Menu", 900, 600, master_ui, state)
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/self/specialize/ui_data(mob/user)
|
||||
var/datum/vampire/vamp = user.mind.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
|
||||
|
||||
if(vamp.subclass)
|
||||
vamp.upgrade_tiers -= type
|
||||
vamp.remove_ability(src)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("umbrae")
|
||||
vamp.add_subclass(SUBCLASS_UMBRAE)
|
||||
vamp.upgrade_tiers -= type
|
||||
vamp.remove_ability(src)
|
||||
if("hemomancer")
|
||||
vamp.add_subclass(SUBCLASS_HEMOMANCER)
|
||||
vamp.upgrade_tiers -= type
|
||||
vamp.remove_ability(src)
|
||||
if("gargantua")
|
||||
vamp.add_subclass(SUBCLASS_GARGANTUA)
|
||||
vamp.upgrade_tiers -= type
|
||||
vamp.remove_ability(src)
|
||||
|
||||
|
||||
/datum/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)
|
||||
SSblackbox.record_feedback("nested tally", "vampire_subclasses", 1, list("[new_subclass.name]"))
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/glare
|
||||
name = "Glare"
|
||||
desc = "Your eyes flash, stunning and silencing anyone infront of you. It has lesser effects for those around you."
|
||||
action_icon_state = "vampire_glare"
|
||||
charge_max = 30 SECONDS
|
||||
stat_allowed = TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/glare/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/T = new
|
||||
T.allowed_type = /mob/living
|
||||
T.range = 1
|
||||
return T
|
||||
|
||||
/// No deviation at all. Flashed from the front or front-left/front-right. Alternatively, flashed in direct view.
|
||||
#define DEVIATION_NONE 3
|
||||
/// Partial deviation. Flashed from the side. Alternatively, flashed out the corner of your eyes.
|
||||
#define DEVIATION_PARTIAL 2
|
||||
/// Full deviation. Flashed from directly behind or behind-left/behind-rack. Not flashed at all.
|
||||
#define DEVIATION_FULL 1
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/glare/cast(list/targets, mob/living/user = usr)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(istype(H.glasses, /obj/item/clothing/glasses/sunglasses/blindfold))
|
||||
var/obj/item/clothing/glasses/sunglasses/blindfold/B = H.glasses
|
||||
if(B.tint)
|
||||
to_chat(user, "<span class='warning'>You're blindfolded!</span>")
|
||||
return
|
||||
user.mob_light(LIGHT_COLOR_BLOOD_MAGIC, 3, _duration = 2)
|
||||
user.visible_message("<span class='warning'>[user]'s eyes emit a blinding flash!</span>")
|
||||
|
||||
for(var/mob/living/target in targets)
|
||||
if(!target.affects_vampire(user))
|
||||
continue
|
||||
|
||||
var/deviation
|
||||
if(user.weakened || user.resting)
|
||||
deviation = DEVIATION_PARTIAL
|
||||
else
|
||||
deviation = calculate_deviation(target, user)
|
||||
|
||||
if(deviation == DEVIATION_FULL)
|
||||
target.AdjustConfused(3)
|
||||
target.adjustStaminaLoss(40)
|
||||
else if(deviation == DEVIATION_PARTIAL)
|
||||
target.Weaken(1)
|
||||
target.AdjustConfused(3)
|
||||
target.adjustStaminaLoss(40)
|
||||
else
|
||||
target.adjustStaminaLoss(120)
|
||||
target.Weaken(6)
|
||||
target.AdjustSilence(3)
|
||||
target.flash_eyes(1, TRUE, TRUE)
|
||||
to_chat(target, "<span class='warning'>You are blinded by [user]'s glare.</span>")
|
||||
add_attack_logs(user, target, "(Vampire) Glared at")
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/glare/proc/calculate_deviation(mob/victim, mob/attacker)
|
||||
// Are they on the same tile? We'll return partial deviation. This may be someone flashing while lying down
|
||||
if(victim.loc == attacker.loc)
|
||||
return DEVIATION_PARTIAL
|
||||
|
||||
// If the victim was looking at the attacker, this is the direction they'd have to be facing.
|
||||
var/attacker_to_victim = get_dir(attacker, victim)
|
||||
// The victim's dir is necessarily a cardinal value.
|
||||
var/attacker_dir = attacker.dir
|
||||
|
||||
// - - -
|
||||
// - V - Attacker facing south
|
||||
// # # #
|
||||
// Attacker within 45 degrees of where the victim is facing.
|
||||
if(attacker_dir & attacker_to_victim)
|
||||
return DEVIATION_NONE
|
||||
|
||||
// # # #
|
||||
// - V - Attacker facing south
|
||||
// - - -
|
||||
// Victim at 135 or more degrees of where the victim is facing.
|
||||
if(attacker_dir & GetOppositeDir(attacker_to_victim))
|
||||
return DEVIATION_FULL
|
||||
|
||||
// - - -
|
||||
// # V # Attacker facing south
|
||||
// - - -
|
||||
// Victim lateral to the victim.
|
||||
return DEVIATION_PARTIAL
|
||||
|
||||
#undef DEVIATION_NONE
|
||||
#undef DEVIATION_PARTIAL
|
||||
#undef DEVIATION_FULL
|
||||
|
||||
/datum/vampire_passive/regen
|
||||
gain_desc = "Your rejuvenation abilities have improved and will now heal you over time when used."
|
||||
|
||||
/datum/vampire_passive/vision
|
||||
gain_desc = "Your vampiric vision has improved."
|
||||
|
||||
/datum/vampire_passive/full
|
||||
gain_desc = "You have reached your full potential. You are no longer weak to the effects of anything holy and your vision has improved greatly."
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/raise_vampires
|
||||
name = "Raise Vampires"
|
||||
desc = "Summons deadly vampires from bluespace."
|
||||
school = "transmutation"
|
||||
charge_max = 100
|
||||
clothes_req = 0
|
||||
human_req = 1
|
||||
invocation = "none"
|
||||
invocation_type = "none"
|
||||
cooldown_min = 20
|
||||
action_icon_state = "revive_thrall"
|
||||
sound = 'sound/magic/wandodeath.ogg'
|
||||
gain_desc = "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed."
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/raise_vampires/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/T = new
|
||||
T.range = 3
|
||||
return T
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/raise_vampires/cast(list/targets, mob/user = usr)
|
||||
new /obj/effect/temp_visual/cult/sparks(user.loc)
|
||||
var/turf/T = get_turf(user)
|
||||
to_chat(user, "<span class='warning'>You call out within bluespace, summoning more vampiric spirits to aid you!</span>")
|
||||
for(var/mob/living/carbon/human/H in targets)
|
||||
T.Beam(H, "sendbeam", 'icons/effects/effects.dmi', time = 30, maxdistance = 7, beam_type = /obj/effect/ebeam)
|
||||
new /obj/effect/temp_visual/cult/sparks(H.loc)
|
||||
raise_vampire(user, H)
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/raise_vampires/proc/raise_vampire(mob/M, mob/living/carbon/human/H)
|
||||
if(!istype(M) || !istype(H))
|
||||
return
|
||||
if(!H.mind)
|
||||
visible_message("[H] looks to be too stupid to understand what is going on.")
|
||||
return
|
||||
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)
|
||||
visible_message("<span class='notice'>[H] looks refreshed!</span>")
|
||||
H.adjustBruteLoss(-60)
|
||||
H.adjustFireLoss(-60)
|
||||
for(var/obj/item/organ/external/E in H.bodyparts)
|
||||
if(prob(25))
|
||||
E.mend_fracture()
|
||||
E.fix_internal_bleeding()
|
||||
|
||||
return
|
||||
if(H.stat != DEAD)
|
||||
if(H.IsWeakened())
|
||||
visible_message("<span class='warning'>[H] looks to be in pain!</span>")
|
||||
H.adjustBrainLoss(60)
|
||||
else
|
||||
visible_message("<span class='warning'>[H] looks to be stunned by the energy!</span>")
|
||||
H.Weaken(20)
|
||||
return
|
||||
for(var/obj/item/implant/mindshield/L in H)
|
||||
if(L && L.implanted)
|
||||
qdel(L)
|
||||
for(var/obj/item/implant/traitor/T in H)
|
||||
if(T && T.implanted)
|
||||
qdel(T)
|
||||
visible_message("<span class='warning'>[H] gets an eerie red glow in their eyes!</span>")
|
||||
var/datum/objective/protect/protect_objective = new
|
||||
protect_objective.owner = H.mind
|
||||
protect_objective.target = M.mind
|
||||
protect_objective.explanation_text = "Protect [M.real_name]."
|
||||
H.mind.objectives += protect_objective
|
||||
add_attack_logs(M, H, "Vampire-sired")
|
||||
H.mind.make_vampire()
|
||||
H.revive()
|
||||
H.Weaken(20)
|
||||
|
||||
/obj/effect/proc_holder/spell/turf_teleport/shadow_step
|
||||
name = "Shadow Step (30)"
|
||||
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
|
||||
clothes_req = FALSE
|
||||
centcom_cancast = FALSE
|
||||
include_space = FALSE
|
||||
panel = "Vampire"
|
||||
school = "vampire"
|
||||
action_background_icon_state = "bg_vampire"
|
||||
|
||||
// Teleport radii
|
||||
inner_tele_radius = 0
|
||||
outer_tele_radius = 6
|
||||
|
||||
include_light_turfs = FALSE
|
||||
|
||||
sound1 = null
|
||||
sound2 = null
|
||||
|
||||
/obj/effect/proc_holder/spell/turf_teleport/shadow_step/create_new_handler()
|
||||
var/datum/spell_handler/vampire/H = new
|
||||
H.required_blood = 30
|
||||
return H
|
||||
|
||||
// pure adminbus at the moment
|
||||
/proc/isvampirethrall(mob/living/M)
|
||||
return istype(M) && M.mind && SSticker.mode && (M.mind in SSticker.mode.vampire_enthralled)
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/enthrall
|
||||
name = "Enthrall (150)"
|
||||
desc = "You use a large portion of your power to sway those loyal to none to be loyal to you only."
|
||||
gain_desc = "You have gained the ability to thrall people to your will."
|
||||
action_icon_state = "vampire_enthrall"
|
||||
required_blood = 150
|
||||
deduct_blood_on_cast = FALSE
|
||||
panel = "Vampire"
|
||||
school = "vampire"
|
||||
action_background_icon_state = "bg_vampire"
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/enthrall/create_new_targeting()
|
||||
var/datum/spell_targeting/click/T = new
|
||||
T.range = 1
|
||||
T.click_radius = 0
|
||||
return T
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/enthrall/cast(list/targets, mob/user = usr)
|
||||
var/datum/vampire/vampire = user.mind.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>")
|
||||
if(do_mob(user, target, 50))
|
||||
if(can_enthrall(user, target))
|
||||
handle_enthrall(user, target)
|
||||
var/datum/spell_handler/vampire/V = custom_handler
|
||||
var/blood_cost = V.calculate_blood_cost(vampire)
|
||||
vampire.bloodusable -= blood_cost //we take the blood after enthralling, not before
|
||||
else
|
||||
revert_cast(user)
|
||||
to_chat(user, "<span class='warning'>You or your target either moved or you dont have enough usable blood.</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/enthrall/proc/can_enthrall(mob/living/user, mob/living/carbon/C)
|
||||
var/enthrall_safe = 0
|
||||
for(var/obj/item/implant/mindshield/L in C)
|
||||
if(L && L.implanted)
|
||||
enthrall_safe = 1
|
||||
break
|
||||
for(var/obj/item/implant/traitor/T in C)
|
||||
if(T && T.implanted)
|
||||
enthrall_safe = 1
|
||||
break
|
||||
if(!C)
|
||||
log_runtime(EXCEPTION("something bad happened on enthralling a mob, attacker is [user] [user.key] \ref[user]"), user)
|
||||
return FALSE
|
||||
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))
|
||||
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))
|
||||
if(C.mind.isholy)
|
||||
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>Your faith in [SSticker.Bible_deity_name] has kept your mind clear of all evil.</span>")
|
||||
else
|
||||
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>You resist the attack on your mind.</span>")
|
||||
return FALSE
|
||||
if(!ishuman(C))
|
||||
to_chat(user, "<span class='warning'>You can only enthrall sentient humanoids!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/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")
|
||||
|
||||
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>")
|
||||
H.Stun(2)
|
||||
add_attack_logs(user, H, "Vampire-thralled")
|
||||
@@ -1,70 +0,0 @@
|
||||
/datum/vampire_subclass
|
||||
/// The subclass' name. Used for blackbox logging.
|
||||
var/name = "yell at coderbus"
|
||||
/// A list of powers that a vampire unlocks. The value of the list entry is equal to the blood total required for the vampire to unlock it.
|
||||
var/list/standard_powers
|
||||
/// A list of the powers a vampire unlocks when it reaches full power.
|
||||
var/list/fully_powered_abilities
|
||||
/// 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)
|
||||
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)
|
||||
for(var/thing in fully_powered_abilities)
|
||||
vamp.add_ability(thing)
|
||||
|
||||
/datum/vampire_subclass/umbrae
|
||||
name = "umbrae"
|
||||
standard_powers = list(/obj/effect/proc_holder/spell/vampire/self/cloak = 150,
|
||||
/obj/effect/proc_holder/spell/vampire/shadow_snare = 250,
|
||||
/obj/effect/proc_holder/spell/vampire/dark_passage = 400,
|
||||
/obj/effect/proc_holder/spell/vampire/vamp_extinguish = 600)
|
||||
fully_powered_abilities = list(/datum/vampire_passive/full,
|
||||
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness,
|
||||
/datum/vampire_passive/xray)
|
||||
|
||||
/datum/vampire_subclass/hemomancer
|
||||
name = "hemomancer"
|
||||
standard_powers = list(/obj/effect/proc_holder/spell/vampire/self/vamp_claws = 150,
|
||||
/obj/effect/proc_holder/spell/vampire/blood_tendrils = 250,
|
||||
/obj/effect/proc_holder/spell/ethereal_jaunt/blood_pool = 400,
|
||||
/obj/effect/proc_holder/spell/vampire/blood_eruption = 600)
|
||||
fully_powered_abilities = list(/datum/vampire_passive/full,
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_spill)
|
||||
|
||||
/datum/vampire_subclass/gargantua
|
||||
name = "gargantua"
|
||||
standard_powers = list(/obj/effect/proc_holder/spell/vampire/self/blood_swell = 150,
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_rush = 250,
|
||||
/datum/vampire_passive/blood_swell_upgrade = 400,
|
||||
/obj/effect/proc_holder/spell/vampire/self/overwhelming_force = 600)
|
||||
fully_powered_abilities = list(/datum/vampire_passive/full,
|
||||
/obj/effect/proc_holder/spell/vampire/charge)
|
||||
improved_rejuv_healing = TRUE
|
||||
|
||||
/datum/vampire_subclass/ancient
|
||||
name = "ancient"
|
||||
standard_powers = list(/obj/effect/proc_holder/spell/vampire/self/vamp_claws,
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_swell,
|
||||
/obj/effect/proc_holder/spell/vampire/self/cloak,
|
||||
/obj/effect/proc_holder/spell/vampire/blood_tendrils,
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_rush,
|
||||
/obj/effect/proc_holder/spell/vampire/shadow_snare,
|
||||
/obj/effect/proc_holder/spell/ethereal_jaunt/blood_pool,
|
||||
/datum/vampire_passive/blood_swell_upgrade,
|
||||
/obj/effect/proc_holder/spell/vampire/dark_passage,
|
||||
/obj/effect/proc_holder/spell/vampire/blood_eruption,
|
||||
/obj/effect/proc_holder/spell/vampire/self/overwhelming_force,
|
||||
/obj/effect/proc_holder/spell/vampire/vamp_extinguish,
|
||||
/obj/effect/proc_holder/spell/vampire/raise_vampires,
|
||||
/obj/effect/proc_holder/spell/vampire/enthrall,
|
||||
/datum/vampire_passive/full,
|
||||
/obj/effect/proc_holder/spell/vampire/self/blood_spill,
|
||||
/obj/effect/proc_holder/spell/vampire/charge,
|
||||
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness,
|
||||
/datum/vampire_passive/xray)
|
||||
improved_rejuv_healing = TRUE
|
||||
Reference in New Issue
Block a user