mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-09 07:57:50 +00:00
* Pref code refactor * Empty database reference * Unit testing SQLite * Everything else * Disable unit testing. * Equivalent * more robust unit tests
729 lines
29 KiB
Plaintext
729 lines
29 KiB
Plaintext
#define MAX_BLOOD_PER_TARGET 200
|
|
#define BLOOD_UNIT_DRAIN_MULTIPLIER 2 //How many units of blood get drained from victim at a time per point of blood the vampire gains
|
|
|
|
/datum/role/vampire
|
|
id = VAMPIRE
|
|
name = VAMPIRE
|
|
special_role = VAMPIRE
|
|
disallow_job = FALSE
|
|
restricted_jobs = list("AI", "Cyborg", "Mobile MMI", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain")
|
|
logo_state = "vampire-logo"
|
|
greets = list(GREET_DEFAULT,GREET_CUSTOM,GREET_ADMINTOGGLE, GREET_MASTER)
|
|
required_pref = VAMPIRE
|
|
protected_traitor_prob = PROB_PROTECTED_RARE
|
|
default_admin_voice = "Vampire Overlord"
|
|
admin_voice_style = "danger"
|
|
|
|
var/iscloaking = FALSE
|
|
var/silentbite = FALSE
|
|
var/nullified = 0
|
|
var/smitecounter = 0
|
|
|
|
var/reviving = FALSE
|
|
var/draining = FALSE
|
|
var/blood_usable = STARTING_BLOOD
|
|
var/blood_total = STARTING_BLOOD
|
|
|
|
var/list/feeders = list()
|
|
|
|
var/list/image/cached_images = list()
|
|
|
|
stat_datum_type = /datum/stat/role/vampire
|
|
|
|
/datum/role/vampire/New(var/datum/mind/M, var/datum/faction/fac=null, var/new_id, var/override = FALSE)
|
|
..()
|
|
var/datum/faction/vampire/vamp_fac
|
|
if(!fac)
|
|
vamp_fac = new
|
|
vamp_fac.addMaster(src)
|
|
else if (istype(fac, /datum/faction/vampire))
|
|
vamp_fac = fac
|
|
vamp_fac.addMaster(src)
|
|
wikiroute = role_wiki[VAMPIRE]
|
|
|
|
/datum/role/vampire/Greet(var/greeting,var/custom)
|
|
if(!greeting)
|
|
return
|
|
|
|
var/icon/logo = icon('icons/logos.dmi', logo_state)
|
|
switch(greeting)
|
|
if (GREET_CUSTOM)
|
|
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> [custom]")
|
|
if (GREET_ADMINTOGGLE)
|
|
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> <span class='danger'>Your powers are awoken. Your lust for blood grows... You are a Vampire!</span></B>")
|
|
else
|
|
to_chat(antag.current, "<img src='data:image/png;base64,[icon2base64(logo)]' style='position: relative; top: 10;'/> <span class='danger'>You are a Vampire!<br/></span>")
|
|
to_chat(antag.current, "To drink blood from somebody, just bite their head (switch to harm intent, enable biting and attack the victim in the head with an empty hand).")
|
|
to_chat(antag.current, "Drink blood to gain new powers and use coffins to regenerate your body if injured.")
|
|
to_chat(antag.current, "You are weak to holy things and starlight.")
|
|
to_chat(antag.current, "Don't go into space and avoid the Chaplain, the chapel, and especially Holy Water.")
|
|
to_chat(antag.current, "You will easily recognise the wearers of holy artifacts. Your powers will stop working against them as you go stronger.")
|
|
to_chat(antag.current, "<span class='info'><a HREF='?src=\ref[antag.current];getwiki=[wikiroute]'>(Wiki Guide)</a></span>")
|
|
antag.current << sound('sound/effects/vampire_intro.ogg')
|
|
|
|
/datum/role/vampire/OnPostSetup()
|
|
. = ..()
|
|
update_vamp_hud()
|
|
ForgeObjectives()
|
|
check_vampire_upgrade()
|
|
|
|
if(faction && istype(faction, /datum/faction/vampire) && faction.leader == src)
|
|
var/datum/faction/vampire/V = faction
|
|
V.name_clan(src)
|
|
|
|
/datum/role/vampire/RemoveFromRole(var/datum/mind/M)
|
|
var/list/vamp_spells = getAllVampSpells()
|
|
for(var/spell/spell in antag.current.spell_list)
|
|
if (is_type_in_list(spell, vamp_spells))
|
|
antag.current.remove_spell(spell)
|
|
if(antag.current.client && antag.current.hud_used)
|
|
if(antag.current.hud_used.vampire_blood_display)
|
|
antag.current.client.screen -= list(antag.current.hud_used.vampire_blood_display)
|
|
..()
|
|
|
|
/datum/role/vampire/AdminPanelEntry(var/show_logo = FALSE,var/datum/admins/A)
|
|
var/dat = ..()
|
|
dat += " - <a href='?src=\ref[src]&mind=\ref[antag]&giveblood=1'>Give blood</a>"
|
|
return dat
|
|
|
|
/datum/role/vampire/RoleTopic(href, href_list, var/datum/mind/M, var/admin_auth)
|
|
..()
|
|
if (!usr.client.holder)
|
|
return FALSE
|
|
if (href_list["giveblood"])
|
|
var/amount = input("How much would you like to give?", "Giving blood") as null|num
|
|
if (!amount)
|
|
return FALSE
|
|
give_blood(amount)
|
|
|
|
/datum/role/vampire/proc/give_blood(var/amount)
|
|
blood_total += amount
|
|
blood_usable += amount
|
|
check_vampire_upgrade()
|
|
update_vamp_hud()
|
|
|
|
/datum/role/vampire/GetScoreboard()
|
|
. = "Total blood collected: <b>[blood_total]</b><br/>"
|
|
. += ..() // Who he was, his objectives...
|
|
|
|
/datum/role/vampire/ForgeObjectives()
|
|
if(!antag.current.client.prefs.get_pref(/datum/preference_setting/toggle/antag_objectives))
|
|
AppendObjective(/datum/objective/freeform/vampire)
|
|
return
|
|
|
|
AppendObjective(/datum/objective/acquire_blood)
|
|
|
|
AppendObjective(/datum/objective/target/assassinate/delay_medium)// 10 minutes
|
|
|
|
AppendObjective(/datum/objective/target/steal)
|
|
|
|
if(prob(80))
|
|
if (!(locate(/datum/objective/escape) in objectives.objectives)) // Objectives (the objective holder).objectives (the objective list)
|
|
AppendObjective(/datum/objective/escape)
|
|
else
|
|
if (!(locate(/datum/objective/survive) in objectives.objectives))
|
|
AppendObjective(/datum/objective/survive)
|
|
return
|
|
|
|
// -- Vampire mechanics --
|
|
/datum/role/vampire/proc/can_suck(var/mob/living/carbon/human/H)
|
|
var/mob/living/M = antag.current
|
|
var/datum/butchering_product/teeth/vampire_teeth = locate(/datum/butchering_product/teeth) in M.butchering_drops
|
|
|
|
if(M.lying || M.incapacitated())
|
|
to_chat(M, "<span class='warning'> You cannot do this while on the ground!</span>")
|
|
return FALSE
|
|
|
|
//if(H.check_body_part_coverage(MOUTH))
|
|
// if(!locate(/datum/power/vampire/mature) in current_powers)
|
|
// to_chat(M, "<span class='warning'>Remove their mask!</span>")
|
|
// return FALSE
|
|
|
|
if(vampire_teeth?.amount == 0)
|
|
to_chat(M, "<span class='warning'>You cannot suck blood with no teeth!</span>")
|
|
return FALSE
|
|
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/vamp_H = M
|
|
|
|
if(vamp_H.is_muzzled())
|
|
to_chat(M, "<span class='warning'> The [vamp_H.wear_mask] prevents you from biting!</span>")
|
|
return FALSE
|
|
|
|
if(vamp_H.check_body_part_coverage(MOUTH))
|
|
to_chat(M, "<span class='notice'>With practiced ease, you shift aside your mask for each gulp of blood.</span>")
|
|
|
|
return TRUE
|
|
|
|
/datum/role/vampire/proc/handle_bloodsucking(var/mob/living/carbon/human/target)
|
|
draining = target
|
|
|
|
var/mob/assailant = antag.current
|
|
var/targetref = "\ref[target]"
|
|
var/blood = 0 //How much blood will be sucked
|
|
var/blood_total_before = blood_total
|
|
var/blood_usable_before = blood_usable
|
|
assailant.attack_log += text("\[[time_stamp()]\] <font color='red'>Bit [key_name(target)] in the neck and draining their blood.</font>")
|
|
target.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been bit in the neck by [key_name(assailant)].</font>")
|
|
log_attack("[key_name(assailant)] bit [key_name(target)] in the neck")
|
|
|
|
if(!silentbite)
|
|
to_chat(antag.current, "<span class='danger'>You latch on firmly to \the [target]'s neck.</span>")
|
|
target.show_message("<span class='userdanger'>\The [assailant] latches on to your neck!</span>")
|
|
else
|
|
to_chat(antag.current, "<span class='warning'>You quietly latch on to \the [target]'s neck...")
|
|
|
|
target.assaulted_by(assailant)
|
|
var/initial_silentbite = silentbite //No switching bite types after latching onto someone
|
|
while(do_mob(assailant, target, (5 SECONDS) * (initial_silentbite + 1)))
|
|
if(!isvampire(assailant))
|
|
to_chat(assailant, "<span class='warning'>Your fangs have disappeared!</span>")
|
|
draining = null
|
|
return FALSE
|
|
if(target.species.anatomy_flags & NO_BLOOD)
|
|
to_chat(assailant, "<span class='warning'>Not a drop of blood here.</span>")
|
|
draining = null
|
|
return FALSE
|
|
if(!target.mind)
|
|
to_chat(assailant, "<span class='warning'>This blood is lifeless and has no power.</span>")
|
|
draining = null
|
|
return FALSE
|
|
if(!target.vessel.get_reagent_amount(BLOOD))
|
|
to_chat(assailant, "<span class='warning'>They've got no blood left to give.</span>")
|
|
break
|
|
if (!(targetref in feeders))
|
|
feeders[targetref] = 0
|
|
var/mature = locate(/datum/power/vampire/mature) in current_powers
|
|
if(target.stat < DEAD) //alive
|
|
blood = min(mature ? 40 : 20, target.vessel.get_reagent_amount(BLOOD)/BLOOD_UNIT_DRAIN_MULTIPLIER) // if they have less than 20 blood, give them the remnant else they get 20 blood
|
|
if (feeders[targetref] < MAX_BLOOD_PER_TARGET)
|
|
blood_total += blood
|
|
else
|
|
to_chat(assailant, "<span class='warning'>Their blood quenches your thirst but won't let you become any stronger. You need to find new prey.</span>")
|
|
blood_usable += blood
|
|
if(!initial_silentbite) //If the bite is silent, do not tip the target that something is wrong.
|
|
target.adjustBruteLoss(1)
|
|
var/datum/organ/external/head/head_organ = target.get_organ(LIMB_HEAD)
|
|
head_organ.add_autopsy_data("sharp teeth", 1)
|
|
else
|
|
blood = min(mature ? 20 : 10, target.vessel.get_reagent_amount(BLOOD)/BLOOD_UNIT_DRAIN_MULTIPLIER) // The dead only give 10 blood
|
|
if (feeders[targetref] < MAX_BLOOD_PER_TARGET)
|
|
blood_total += blood
|
|
else
|
|
to_chat(assailant, "<span class='warning'>Their blood quenches your thirst but won't let you become any stronger. You need to find new prey.</span>")
|
|
feeders[targetref] += blood
|
|
if(blood_total_before != blood_total)
|
|
to_chat(assailant, "<span class='notice'>You have accumulated [blood_total] [blood_total > 1 ? "units" : "unit"] of blood[blood_usable_before != blood_usable ?", and have [blood_usable] left to use." : "."]</span>")
|
|
check_vampire_upgrade()
|
|
target.vessel.remove_reagent(BLOOD,blood * BLOOD_UNIT_DRAIN_MULTIPLIER)
|
|
var/mob/living/carbon/V = assailant
|
|
if(V)
|
|
var/fatty_chemicals = target.reagents.has_any_reagents(list(CHEESYGLOOP, CORNOIL)) //If the target has these chemicals in his blood the vampire can get fat from sucking blood.
|
|
var/eating_threshold = fatty_chemicals ? OVEREAT_THRESHOLD * 2 : OVEREAT_THRESHOLD
|
|
if(V.nutrition < eating_threshold) //Gives the vampire a little bit of food, at a rate of 1/4 the blood sucked.
|
|
V.nutrition = round(min(V.nutrition + blood/4, OVEREAT_THRESHOLD), 1)
|
|
update_vamp_hud()
|
|
|
|
draining = null
|
|
to_chat(assailant, "<span class='notice'>You stop draining \the [target] of blood.</span>")
|
|
return TRUE
|
|
|
|
/datum/role/vampire/proc/check_vampire_upgrade()
|
|
|
|
for (var/i in subtypesof(/datum/power/vampire))
|
|
var/datum/power/vampire/VP_type = i
|
|
if (blood_total > initial(VP_type.cost) && !(locate(VP_type) in current_powers))
|
|
var/datum/power/vampire/VP = new VP_type
|
|
VP.add_power(src)
|
|
|
|
var/mob/living/carbon/human/H = antag.current
|
|
if (!istype(H))
|
|
return
|
|
|
|
// Vision-related changes.
|
|
if(!count_by_type(H.huds, /datum/visioneffect/nullrod))
|
|
H.apply_hud(new /datum/visioneffect/nullrod)
|
|
if (locate(/datum/power/vampire/vision) in current_powers)
|
|
if(!count_by_type(H.huds, /datum/visioneffect/vampire_improved))
|
|
H.apply_hud(new /datum/visioneffect/vampire_improved)
|
|
else
|
|
H.remove_hud_by_type(/datum/visioneffect/vampire_improved)
|
|
|
|
if (locate(/datum/power/vampire/mature) in current_powers)
|
|
if(!count_by_type(H.huds, /datum/visioneffect/vampire_mature))
|
|
H.apply_hud(new /datum/visioneffect/vampire_mature)
|
|
else
|
|
H.remove_hud_by_type(/datum/visioneffect/vampire_mature)
|
|
|
|
H.handle_hud_vision_updates()
|
|
|
|
/datum/role/vampire/update_perception()
|
|
return
|
|
|
|
/datum/role/vampire/proc/is_mature_or_has_vision()
|
|
return (locate(/datum/power/vampire/vision) in current_powers) || (locate(/datum/power/vampire/mature) in current_powers)
|
|
|
|
/datum/role/vampire/proc/handle_enthrall(var/datum/mind/enthralled)
|
|
if (!istype(enthralled))
|
|
return FALSE
|
|
return new/datum/role/thrall(M = enthralled, fac = src.faction, master = src) // Creating a new thrall
|
|
|
|
//-- Life() related procs --
|
|
/datum/role/vampire/process()
|
|
..()
|
|
var/mob/living/carbon/human/H = antag?.current
|
|
if (!istype(H))
|
|
return FALSE // The life() procs only work on humans.
|
|
handle_cloak(H)
|
|
handle_menace(H)
|
|
handle_smite(H)
|
|
if(istype(H.loc, /turf/space))
|
|
H.check_sun()
|
|
if(istype(H.loc, /obj/structure/closet/coffin))
|
|
H.adjustBruteLoss(-4)
|
|
H.adjustFireLoss(-4)
|
|
H.adjustToxLoss(-4)
|
|
H.adjustOxyLoss(-4)
|
|
smitecounter = 0
|
|
nullified -= 5
|
|
for(var/datum/organ/internal/I in H.internal_organs)
|
|
if(I && I.damage > 0)
|
|
I.damage = max(0, I.damage - 4)
|
|
if(I)
|
|
I.status &= ~ORGAN_BROKEN
|
|
I.status &= ~ORGAN_SPLINTED
|
|
I.status &= ~ORGAN_BLEEDING
|
|
for(var/datum/organ/external/O in H.organs)
|
|
O.status &= ~ORGAN_BROKEN
|
|
O.status &= ~ORGAN_SPLINTED
|
|
O.status &= ~ORGAN_BLEEDING
|
|
nullified = max(0, nullified - 1)
|
|
|
|
/datum/role/vampire/proc/handle_cloak(var/mob/living/carbon/human/H)
|
|
var/turf/T = get_turf(H)
|
|
if(H.stat == DEAD)
|
|
iscloaking = FALSE
|
|
if(!iscloaking)
|
|
H.make_visible(VAMPIRECLOAK,TRUE)
|
|
H.color = "#FFFFFF"
|
|
return FALSE
|
|
|
|
if((T.get_lumcount() * 10) <= 2)
|
|
if(locate(/datum/power/vampire/mature) in current_powers)
|
|
H.make_invisible(VAMPIRECLOAK, 0, TRUE, round(255 * 0.3), INVISIBILITY_LEVEL_TWO)
|
|
else
|
|
H.make_invisible(VAMPIRECLOAK, 0, TRUE, round(255 * 0.3))
|
|
return TRUE
|
|
else
|
|
if(H.invisibility > 0)
|
|
H.make_visible(VAMPIRECLOAK)
|
|
if(locate(/datum/power/vampire/mature) in current_powers)
|
|
H.make_invisible(VAMPIRECLOAK, 0, TRUE, round(255 * 0.3))
|
|
else
|
|
H.make_invisible(VAMPIRECLOAK, 0, TRUE, round(255 * 0.8))
|
|
|
|
/datum/role/vampire/proc/handle_menace(var/mob/living/carbon/human/H)
|
|
if(!istruevampire(H))
|
|
return
|
|
if(H.stat == DEAD)
|
|
return
|
|
var/turf/T = get_turf(H)
|
|
if(T.get_lumcount() > 2)
|
|
return
|
|
|
|
for(var/mob/living/carbon/C in oviewers(6, antag.current))
|
|
if(prob(35))
|
|
continue //to prevent fearspam
|
|
var/datum/role/thrall/role_thrall = isthrall(C)
|
|
if (role_thrall && role_thrall.master == src)
|
|
continue // We don't terrify our underlings
|
|
if (C.vampire_affected(antag) <= 0)
|
|
continue
|
|
C.stuttering = max(C.stuttering, 20)
|
|
C.Jitter(20)
|
|
C.Dizzy(20)
|
|
to_chat(C, "<span class='sinister'>Your heart is filled with dread, and you shake uncontrollably.</span>")
|
|
|
|
/datum/role/vampire/proc/handle_smite(var/mob/living/carbon/human/H)
|
|
var/smitetemp = 0
|
|
var/vampcoat = istype(H.wear_suit, /obj/item/clothing/suit/storage/draculacoat) //coat reduces smiting
|
|
if(check_holy(H)) //if you're on a holy tile get ready for pain
|
|
smitetemp += (vampcoat ? 1 : 5)
|
|
if(prob(35))
|
|
to_chat(H, "<span class='danger'>This ground is blessed. Get away, or splatter it with blood to make it safe for you.</span>")
|
|
|
|
if((locate(/datum/power/vampire/mature) in current_powers) && (istype(get_area(H), /area/chapel))) //stay out of the chapel unless you want to turn into a pile of ashes
|
|
nullified = max(5, nullified + 2)
|
|
if(prob(35))
|
|
to_chat(H, "<span class='sinister'>You feel yourself growing weaker.</span>")
|
|
|
|
if(!nullified) //Checks to see if you can benefit from your vamp current_powers here
|
|
if(!(locate(/datum/power/vampire/mature) in current_powers))
|
|
smitetemp -= 1
|
|
|
|
if(!(locate(/datum/power/vampire/undying) in current_powers))
|
|
smitetemp -= 2
|
|
|
|
if(smitetemp <= 0) //if you weren't smote by the tile you're on, remove a little holy
|
|
smitetemp = -1
|
|
|
|
smitecounter = max(0, (smitecounter + smitetemp))
|
|
|
|
switch(smitecounter)
|
|
if(1 to 30) //just dizziness
|
|
H.dizziness = max(5, H.dizziness)
|
|
if(prob(35))
|
|
to_chat(H, "<span class='warning'>You feel sick.</span>")
|
|
if(30 to 60) //more dizziness, and occasional disorientation
|
|
H.dizziness = max(5, H.dizziness + 1)
|
|
remove_blood(1)
|
|
if(prob(35))
|
|
H.confused = max(5, H.confused)
|
|
to_chat(H, "<span class='warning'>You feel very sick.</span>")
|
|
if(60 to 90) //this is where you start barfing and losing your current_powers
|
|
H.dizziness = max(10, H.dizziness + 3)
|
|
nullified = max(20, nullified)
|
|
remove_blood(2)
|
|
if(prob(8))
|
|
H.vomit()
|
|
if(prob(35))
|
|
H.confused = max(5, H.confused)
|
|
to_chat(H, "<span class='warning'>You feel extremely sick. Get to a coffin as soon as you can.</span>")
|
|
if(90 to 100) //previous effects, and skin starts to smoulder
|
|
H.dizziness = max(10, H.dizziness + 6)
|
|
nullified = max(20, nullified + 1)
|
|
remove_blood(5)
|
|
H.confused = max(10, H.confused)
|
|
H.adjustFireLoss(1)
|
|
if(prob(35))
|
|
H.visible_message("<span class='danger'>[H]'s skin sizzles!</span>", "<span class='danger'>Your skin sizzles!</span>")
|
|
if(100 to (INFINITY)) //BONFIRE
|
|
H.dizziness = max(50, H.dizziness + 8)
|
|
nullified = max(50, nullified + 10)
|
|
remove_blood(10)
|
|
H.confused = max(10, H.confused)
|
|
if(!H.on_fire)
|
|
to_chat(H, "<span class='danger'>Your skin catches fire!</span>")
|
|
else if(prob(35))
|
|
to_chat(H, "<span class='danger'>The holy flames continue to burn your flesh!</span>")
|
|
H.fire_stacks += 5
|
|
H.ignite()
|
|
|
|
/datum/role/vampire/proc/remove_blood(var/amount)
|
|
blood_usable = max(0, blood_usable - amount)
|
|
update_vamp_hud()
|
|
|
|
/datum/role/vampire/PostMindTransfer(var/mob/living/new_character, var/mob/living/old_character)
|
|
. = ..()
|
|
current_powers.Cut()
|
|
if (issilicon(new_character) || isbrain(new_character)) // No, borgs shouldn't be able to spawn bats
|
|
logo_state = "" // Borgos don't get the vampire icon.
|
|
else
|
|
logo_state = initial(logo_state)
|
|
check_vampire_upgrade()
|
|
|
|
/datum/role/vampire/handle_reagent(var/reagent_id)
|
|
switch(reagent_id)
|
|
if (HOLYWATER,INCENSE_HAREBELLS,SACREDWATER)
|
|
var/mob/living/carbon/human/H = antag.current
|
|
if (!istype(H))
|
|
return
|
|
if(locate(/datum/power/vampire/mature) in current_powers)
|
|
to_chat(H, "<span class='danger'>A freezing liquid permeates your bloodstream. Your vampiric powers fade and your insides burn.</span>")
|
|
H.take_organ_damage(0, 5) //FIRE, MAGIC FIRE THAT BURNS ROBOTIC LIMBS TOO!
|
|
smitecounter += 10 //50 units to catch on fire. Generally you'll get fucked up quickly
|
|
else
|
|
to_chat(H, "<span class='warning'>A freezing liquid permeates your bloodstream. You're still too human to be smited!</span>")
|
|
smitecounter += 2 //Basically nothing, unless you drank multiple bottles of holy water (250 units to catch on fire !)
|
|
|
|
/datum/role/vampire/handle_splashed_reagent(var/reagent_id, var/method, var/volume)
|
|
switch (reagent_id)
|
|
if (HOLYWATER,SACREDWATER)
|
|
var/mob/living/carbon/human/H = antag.current
|
|
if (!istype(H))
|
|
return
|
|
if(!(locate(/datum/power/vampire/undying) in current_powers))
|
|
if(method == TOUCH)
|
|
if(H.wear_mask)
|
|
to_chat(H, "<span class='warning'>Your mask protects you from the holy water!</span>")
|
|
return
|
|
|
|
if(H.head)
|
|
to_chat(H, "<span class='warning'>Your helmet protects you from the holy water!</span>")
|
|
return
|
|
|
|
if(H.dissolvable())
|
|
if(prob(15) && volume >= 30)
|
|
var/datum/organ/external/head/head_organ = H.get_organ(LIMB_HEAD)
|
|
if(head_organ)
|
|
if(!(locate(/datum/power/vampire/mature) in current_powers))
|
|
to_chat(H, "<span class='danger'>A freezing liquid covers your face. Its melting!</span>")
|
|
smitecounter += 60 //Equivalent from metabolizing all this holy water normally
|
|
if(head_organ.take_damage(30, 0))
|
|
H.UpdateDamageIcon(1)
|
|
head_organ.disfigure("burn")
|
|
H.audible_scream()
|
|
else
|
|
to_chat(H, "<span class='warning'>A freezing liquid covers your face. Your vampiric current powers protect you!</span>")
|
|
smitecounter += 12 //Ditto above
|
|
|
|
else
|
|
if(!(locate(/datum/power/vampire/mature) in current_powers))
|
|
to_chat(H, "<span class='danger'>You are doused with a freezing liquid. You're melting!</span>")
|
|
H.take_organ_damage(min(15, volume * 2)) //Uses min() and volume to make sure they aren't being sprayed in trace amounts (1 unit != insta rape) -- Doohl
|
|
smitecounter += volume * 2
|
|
else
|
|
to_chat(H, "<span class='warning'>You are doused with a freezing liquid. Your vampiric current powers protect you!</span>")
|
|
smitecounter += volume * 0.4
|
|
else
|
|
if(H.dissolvable())
|
|
H.take_organ_damage(min(15, volume * 2))
|
|
smitecounter += 5
|
|
|
|
/*
|
|
-- Helpers --
|
|
*/
|
|
|
|
/datum/role/vampire/update_antag_hud()
|
|
update_vamp_hud()
|
|
|
|
/datum/role/vampire/proc/update_vamp_hud()
|
|
var/mob/M = antag.current
|
|
if(M && M.client && M.hud_used)
|
|
if(!M.hud_used.vampire_blood_display)
|
|
M.hud_used.vampire_hud()
|
|
//hud_used.human_hud(hud_used.ui_style)
|
|
M.hud_used.vampire_blood_display.maptext_width = WORLD_ICON_SIZE*2
|
|
M.hud_used.vampire_blood_display.maptext_height = WORLD_ICON_SIZE
|
|
M.hud_used.vampire_blood_display.maptext = "<div align='left' valign='top' style='position:relative; top:0px; left:6px'>U:<font color='#33FF33'>[blood_usable]</font><br> T:<font color='#FFFF00'>[blood_total]</font></div>"
|
|
|
|
/mob/living/carbon/human/proc/check_sun()
|
|
var/ax = x
|
|
var/ay = y
|
|
|
|
for(var/i = 1 to 20)
|
|
ax += sun.dx
|
|
ay += sun.dy
|
|
|
|
var/turf/T = locate( round(ax,0.5),round(ay,0.5),z)
|
|
|
|
if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy)
|
|
break
|
|
|
|
if(T.density)
|
|
return
|
|
|
|
if(prob(45))
|
|
switch(health)
|
|
if(80 to 100)
|
|
to_chat(src, "<span class='warning'>Your skin flakes away...</span>")
|
|
adjustFireLoss(1)
|
|
if(60 to 80)
|
|
to_chat(src, "<span class='warning'>Your skin sizzles!</span>")
|
|
adjustFireLoss(1)
|
|
if((-INFINITY) to 60)
|
|
if(!on_fire)
|
|
to_chat(src, "<span class='danger'>Your skin catches fire!</span>")
|
|
else
|
|
to_chat(src, "<span class='danger'>You continue to burn!</span>")
|
|
fire_stacks += 5
|
|
ignite()
|
|
audible_scream()
|
|
else
|
|
switch(health)
|
|
if((-INFINITY) to 60)
|
|
fire_stacks++
|
|
ignite()
|
|
adjustFireLoss(3)
|
|
|
|
/*
|
|
-- Thralls --
|
|
*/
|
|
|
|
/datum/role/thrall
|
|
id = THRALL
|
|
name = "thrall"
|
|
special_role = "thrall"
|
|
logo_state = "thrall-logo"
|
|
|
|
var/datum/role/vampire/master
|
|
|
|
/datum/role/thrall/New(var/datum/mind/M, var/datum/faction/fac=null, var/new_id, var/override = FALSE, var/datum/role/vampire/master)
|
|
. = ..()
|
|
if(!istype(master))
|
|
return FALSE
|
|
src.master = master
|
|
message_admins("[key_name(M)] was enthralled by [key_name(master.antag)]. [formatJumpTo(get_turf(M.current))]")
|
|
log_admin("[key_name(M)] was enthralled by [key_name(master.antag)]. [formatJumpTo(get_turf(M.current))]")
|
|
update_faction_icons()
|
|
Greet(TRUE)
|
|
ForgeObjectives()
|
|
AnnounceObjectives()
|
|
OnPostSetup()
|
|
|
|
/datum/role/thrall/Greet(var/you_are = TRUE)
|
|
var/dat
|
|
if (you_are)
|
|
dat = "<span class='danger'>You are a Thrall!</br> You are slaved to <b>[master.antag.current]</b> [faction?"under [faction.name].":"."]</span>"
|
|
dat += {""}
|
|
to_chat(antag.current, dat)
|
|
to_chat(antag.current, "<B>You must complete the following tasks:</B>")
|
|
antag.current << sound('sound/effects/vampire_intro.ogg')
|
|
|
|
/datum/role/thrall/ForgeObjectives()
|
|
var/datum/objective/target/protect/P = new(auto_target = FALSE)
|
|
P.set_target(master.antag)
|
|
AppendObjective(P)
|
|
|
|
/datum/role/thrall/Drop(var/deconverted = FALSE)
|
|
var/mob/M = antag.current
|
|
message_admins("[key_name(M)] was dethralled, his master was [key_name(master.antag)]. [formatJumpTo(get_turf(antag.current))]")
|
|
log_admin("[key_name(M)] was dethralled, his master was [key_name(master.antag)]. [formatJumpTo(get_turf(antag.current))]")
|
|
if (deconverted)
|
|
M.visible_message("<span class='big danger'>[M] suddenly becomes calm and collected again, \his eyes clear up.</span>",
|
|
"<span class='warning'><b>Your blood cools down and you are inhabited by a sensation of untold calmness.</b></span>")
|
|
to_chat(M, "<span class='big warning'>You are no longer a slave to [master.antag.current]'s whims, having <b>escaped your thralldom.<b></span>")
|
|
update_faction_icons()
|
|
return ..()
|
|
|
|
/datum/role/thrall/handle_reagent(var/reagent_id)
|
|
switch (reagent_id)
|
|
if (HOLYWATER)
|
|
var/mob/living/carbon/human/H = antag.current
|
|
if (!istype(H))
|
|
return
|
|
if (prob(35)) // 35% chance of dethralling
|
|
Drop(TRUE)
|
|
|
|
/mob/proc/vampire_power(var/required_blood = 0, var/max_stat = 0)
|
|
var/datum/role/vampire/vampire = isvampire(src)
|
|
|
|
if(!vampire)
|
|
world.log << "[src] has vampire spells but isn't a vampire."
|
|
return 0
|
|
|
|
var/fullpower = (locate(/datum/power/vampire/jaunt) in vampire.current_powers)
|
|
|
|
if(src.stat > max_stat)
|
|
to_chat(src, "<span class='warning'>You are incapacitated.</span>")
|
|
return 0
|
|
|
|
if(vampire.nullified)
|
|
if(!fullpower)
|
|
to_chat(src, "<span class='warning'>Something is blocking your powers!</span>")
|
|
return 0
|
|
if(vampire.blood_usable < required_blood)
|
|
to_chat(src, "<span class='warning'>You require at least [required_blood] units of usable blood to do that!</span>")
|
|
return 0
|
|
//chapel check
|
|
if(istype(get_area(src), /area/chapel))
|
|
to_chat(src, "<span class='warning'>Your powers are useless on this holy ground.</span>")
|
|
return 0
|
|
if(check_holy(src))
|
|
var/turf/T = get_turf(src)
|
|
if((T.get_lumcount() * 10) > 2)
|
|
to_chat(src, "<span class='warning'>This ground has been blessed and illuminated, suppressing your abilities.</span>")
|
|
return 0
|
|
if (fullpower)
|
|
to_chat(src, "<span class='warning'>Our awakened powers are suppressed on this holy ground.</span>")
|
|
return 0
|
|
return 1
|
|
|
|
/mob/proc/can_enthrall(var/mob/living/carbon/human/H)
|
|
var/datum/role/vampire/V = isvampire(src)
|
|
if(restrained())
|
|
to_chat(src, "<span class ='warning'> You cannot do this while restrained! </span>")
|
|
return 0
|
|
if(!istype(H))
|
|
to_chat(src, "<span class='warning'>You can only enthrall humanoids!</span>")
|
|
return 0
|
|
if(!H)
|
|
message_admins("Error during enthralling: no target. Mob is [src], (<A HREF='?_src_=holder;adminplayerobservejump=\ref[src]&mob=\ref[src]'>JMP</A>)")
|
|
return FALSE
|
|
if(!H.mind)
|
|
to_chat(src, "<span class='warning'>[H]'s mind is not there for you to enthrall.</span>")
|
|
return FALSE
|
|
if(isvampire(H) || isthrall(H))
|
|
H.visible_message("<span class='warning'>[H] seems to resist the takeover!</span>", "<span class='notice'>You feel a familiar sensation in your skull that quickly dissipates.</span>")
|
|
return FALSE
|
|
//Charisma allows implanted targets to be enthralled.
|
|
if(!(locate(/datum/power/vampire/charisma) in V.current_powers) && H.is_loyalty_implanted())
|
|
H.visible_message("<span class='warning'>[H] seems to resist the takeover!</span>", "<span class='notice'>You feel a strange sensation in your skull that quickly dissipates.</span>")
|
|
return FALSE
|
|
if(H.vampire_affected(mind) <= 0)
|
|
H.visible_message("<span class='warning'>[H] seems to resist the takeover!</span>", "<span class='notice'>Your faith of [ticker.Bible_deity_name] has kept your mind clear of all evil!</span>")
|
|
return TRUE
|
|
|
|
/mob/proc/vampire_affected(var/datum/mind/M, var/send_message = TRUE) // M is the attacker, src is the target.
|
|
//Other vampires aren't affected
|
|
var/success = TRUE
|
|
if(mind && mind.GetRole(VAMPIRE))
|
|
return 0
|
|
|
|
// Non-mature vampires are not stopped by holy things.
|
|
if(M)
|
|
//Chaplains are ALWAYS resistant to vampire powers
|
|
if(isReligiousLeader(src))
|
|
if(send_message)
|
|
to_chat(M.current, "<span class='warning'>[src] resists our powers!</span>")
|
|
success = FALSE
|
|
var/mob/thrallcheck
|
|
if(client)
|
|
thrallcheck = src
|
|
else if(mind)
|
|
var/mob/dead/observer/ghost = mind_can_reenter(src.mind)
|
|
if(ghost)
|
|
thrallcheck = ghost.get_top_transmogrification()
|
|
if(isthrall(thrallcheck))
|
|
if(send_message)
|
|
to_chat(M.current, "<span class='warning'>This one's mind is already under the influence of another!</span>")
|
|
return FALSE
|
|
// Null rod nullifies vampire powers, unless we're a young vamp.
|
|
var/datum/role/vampire/V = M.GetRole(VAMPIRE)
|
|
var/obj/item/weapon/nullrod/N = locate(/obj/item/weapon/nullrod) in get_contents_in_object(src)
|
|
if (N)
|
|
if (locate(/datum/power/vampire/undying) in V.current_powers)
|
|
if(send_message)
|
|
to_chat(M.current, "<span class='warning'>A holy artifact has turned our powers against us!</span>")
|
|
success = VAMP_FAILURE
|
|
else if (locate(/datum/power/vampire/jaunt) in V.current_powers)
|
|
if(send_message)
|
|
to_chat(M.current, "<span class='warning'>A holy artifact protects [src]!</span>")
|
|
success = FALSE
|
|
return success
|
|
|
|
// If the target is weakened, the spells take less time to complete.
|
|
/mob/living/carbon/proc/get_vamp_enhancements()
|
|
return ((knockdown ? 2 : 0) + (stunned ? 1 : 0) + (sleeping || paralysis ? 3 : 0))
|
|
|
|
/datum/role/vampire/role_examine_text_addition(target)
|
|
if((!ishuman(antag.current)) || (target == antag.current)) //Vampire is not a human that can suck blood, or trying to examine themselves.
|
|
return
|
|
var/mob/living/carbon/human/H = target
|
|
if(!istype(H)) //Examined target is not a human.
|
|
return
|
|
var/text = ""
|
|
if(isReligiousLeader(H) || (locate(/obj/item/weapon/nullrod) in get_contents_in_object(H))) //Chaplains and protected targets can't be examined
|
|
return "<span class='danger'>[logo_image] This vessel is protected by a holy aura!</span>"
|
|
if(H.species.anatomy_flags & NO_BLOOD) //Target cannot carry blood
|
|
return "<span class='warning'>[logo_image] This vessel is incapable of having any blood!</span>"
|
|
var/amount_of_blood = round(H.vessel.get_reagent_amount(BLOOD), 1)
|
|
var/blood_type = H.dna ? H.dna.b_type : null
|
|
if(amount_of_blood)
|
|
var/targetref = "\ref[H]"
|
|
text += "<span class='notice'>This vessel has <span class='danger'>[amount_of_blood]</span> units of [blood_type ? "[blood_type] " : ""]blood left.</span>"
|
|
if(!H.mind) //Target has no mind
|
|
text += "<span class='warning'><br>This vessel only carries lifeless blood!</span>"
|
|
else if(!(targetref in feeders) || (feeders[targetref] < MAX_BLOOD_PER_TARGET)) //Target has not been fed upon or is below the empowering blood limit they can give
|
|
text += "<span class='good'><br>This vessel's blood can empower you!</span>"
|
|
else
|
|
text += "<span class='warning'><br>This vessel's blood has no energy left for you...</span>"
|
|
if(H.check_body_part_coverage(MOUTH)) //Target is masked
|
|
if(!locate(/datum/power/vampire/mature) in current_powers) //Vampire is not mature enough to deal with that
|
|
text += "<span class='warning'><br>This vessel wears a mask! This will impede you from sucking their blood.</span>"
|
|
else
|
|
text += "<span class='notice'><br>This vessel wears a mask, but this will not impede you because you are a <span class='bnotice'>mature</span> vampire!</span>"
|
|
else
|
|
return "<span class='warning'>[logo_image] This vessel is completely drained of all blood!</span>"
|
|
return "[logo_image] [text]"
|