Files
2020-02-19 19:48:29 -08:00

407 lines
11 KiB
Plaintext

/datum/abilityHolder/vampire/var/list/blood_tally
/datum/abilityHolder/vampire/var/const/max_take_per_mob = 250
/datum/abilityHolder/vampire/proc/tally_bite(var/mob/living/carbon/human/target, var/blood_amt_taken)
if (!src.blood_tally)
src.blood_tally = list()
if (!(target in src.blood_tally))
src.blood_tally[target] = 0
src.blood_tally[target] += blood_amt_taken
/datum/abilityHolder/vampire/proc/can_take_blood_from(var/mob/living/carbon/human/target)
.= 1
if (src.blood_tally)
if (target in src.blood_tally)
.= src.blood_tally[target] < max_take_per_mob
/datum/abilityHolder/vampire/proc/can_bite(var/mob/living/carbon/human/target, is_pointblank = 1)
var/datum/abilityHolder/vampire/holder = src
var/mob/living/M = holder.owner
var/datum/abilityHolder/vampire/H = holder
if (!M || !target)
return 0
if (!ishuman(target)) // Only humans use the blood system.
boutput(M, __red("You can't seem to find any blood vessels."))
return 0
else
var/mob/living/carbon/human/humantarget = target
if (istype(humantarget.mutantrace, /datum/mutantrace/vamp_zombie))
boutput(M, __red("You cannot drink the blood of a thrall."))
return 0
if (M == target)
boutput(M, __red("Why would you want to bite yourself?"))
return 0
if (iscritter(M) && !istype(H))
boutput(M, __red("Critter mobs currently don't have to worry about blood. Lucky you."))
return 0
if (istype(H) && H.vamp_isbiting)
if (vamp_isbiting != target)
boutput(M, __red("You are already draining someone's blood!"))
return 0
if (is_pointblank && target.head && target.head.body_parts_covered & HEAD)
boutput(M, __red("You need to remove their headgear first."))
return 0
if (is_pointblank && target.wear_mask && target.wear_mask.body_parts_covered & HEAD)
boutput(M, __red("You need to remove their facemask first."))
return 0
if (check_target_immunity(target) == 1)
target.visible_message("<span style=\"color:red\"><B>[M] bites [target], but fails to even pierce their skin!</B></span>")
return 0
if ((target.mind && target.mind.special_role == "vampthrall") && target.is_mentally_dominated_by(M))
boutput(M, __red("You can't drink the blood of your own thralls!"))
return 0
if (ismonkey(target) || (target.bioHolder && target.bioHolder.HasEffect("monkey")))
boutput(M, __red("Drink monkey blood?! That's disgusting!"))
return 0
if (!holder.can_take_blood_from(target))
return 0
return 1
/datum/abilityHolder/vampire/proc/do_bite(var/mob/living/carbon/human/HH, var/mult = 1, var/thrall = 0)
.= 1
var/mob/living/carbon/human/M = src.owner
var/datum/abilityHolder/vampire/H = src
if (HH.blood_volume <= 0)
boutput(M, __red("This human is completely void of blood... Wow!"))
return 0
if (isdead(HH))
if (prob(20))
boutput(M, __red("The blood of the dead provides little sustenance..."))
var/bitesize = 5 * mult
M.change_vampire_blood(bitesize, 1)
M.change_vampire_blood(bitesize, 0)
H.tally_bite(HH,bitesize)
if (HH.blood_volume < 20 * mult)
HH.blood_volume = 0
else
HH.blood_volume -= 20 * mult
if (istype(H)) H.blood_tracking_output()
else if (HH.bioHolder && HH.traitHolder.hasTrait("training_chaplain"))
M.visible_message("<span style=\"color:red\"><b>[M]</b> begins to crisp and burn!</span>", "<span style=\"color:red\">You drank the blood of a holy man! It burns!</span>")
M.emote("scream")
if (M.get_vampire_blood() >= 20 * mult)
M.change_vampire_blood(-20 * mult, 0)
else
M.change_vampire_blood(0, 0, 1)
M.TakeDamage("chest", 0, 30 * mult)
else
if (isvampire(HH))
var/bitesize = 20 * mult
if (HH.get_vampire_blood() >= bitesize)
HH.change_vampire_blood(-bitesize, 0)
HH.change_vampire_blood(-bitesize, 1) // Otherwise, two vampires could perpetually feed off of each other, trading blood endlessly.
M.change_vampire_blood(bitesize, 0)
M.change_vampire_blood(bitesize, 1)
H.tally_bite(HH,bitesize)
if (istype(H))
H.blood_tracking_output()
if (prob(50))
boutput(M, __red("This is the blood of a fellow vampire!"))
else
HH.change_vampire_blood(0, 0, 1)
boutput(M, __red("[HH] doesn't have enough blood left to drink."))
return 0
else
var/bitesize = 10 * mult
M.change_vampire_blood(bitesize, 1)
M.change_vampire_blood(bitesize, 0)
H.tally_bite(HH,bitesize)
if (HH.blood_volume < 20 * mult)
HH.blood_volume = 0
else
HH.blood_volume -= 20 * mult
//vampires heal, thralls don't
if (!thrall)
M.HealDamage("All", 3, 3)
M.take_toxin_damage(-1)
M.take_oxygen_deprivation(-1)
if (mult >= 1) //mult is only 1 or greater during a pointblank true suck
if (HH.blood_volume < 300 && prob(15))
if (!HH.getStatusDuration("paralysis"))
boutput(HH, __red("Your vision fades to blackness."))
HH.changeStatus("paralysis", 100)
else
if (prob(65))
HH.changeStatus("weakened", 1 SECOND)
HH.stuttering = min(HH.stuttering + 3, 10)
if (istype(H)) H.blood_tracking_output()
if (!can_take_blood_from(HH) && (mult >= 1) && (isunconscious(HH) || HH.health <= 90))
HH.death(0)
if (istype(H))
H.check_for_unlocks()
eat_twitch(src.owner)
playsound(src.owner.loc,"sound/items/drink.ogg", rand(10,50), 1, pitch = 1.4)
HH.was_harmed(M, special = "vamp")
/datum/targetable/vampire/blood_steal
name = "Blood Steal"
desc = "Steal blood from a victim at range. This ability will continue to channel until you move."
icon_state = "bloodsteal"
targeted = 1
target_nodamage_check = 1
max_range = 999
cooldown = 45 SECONDS
pointCost = 0
when_stunned = 1
not_when_handcuffed = 0
sticky = 1
unlock_message = "You have gained Hide Coffin. It allows you to hide a coffin somewhere on the station."
cast(mob/target)
if (!holder)
return 1
var/mob/living/M = holder.owner
var/datum/abilityHolder/vampire/V = holder
if (actions.hasAction(M, "vamp_blood_suck"))
boutput(M, __red("You are already performing a Bite action and cannot start a Blood Steal."))
return 1
actions.start(new/datum/action/bar/private/icon/vamp_ranged_blood_suc(M,V,target, src), M)
return 0
/datum/action/bar/private/icon/vamp_ranged_blood_suc
duration = 10
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_STUNNED
id = "vamp_blood_suck_ranged"
icon = 'icons/ui/actions.dmi'
icon_state = "blood"
var/mob/living/carbon/human/M
var/datum/abilityHolder/vampire/H
var/mob/living/carbon/human/HH
var/datum/targetable/vampire/blood_steal/ability
New(user,vampabilityholder,target,biteabil)
M = user
H = vampabilityholder
HH = target
ability = biteabil
..()
onUpdate()
..()
if(get_dist(M, HH) > 7 || M == null || HH == null)
interrupt(INTERRUPT_ALWAYS)
return
onStart()
..()
if(M == null || HH == null)
interrupt(INTERRUPT_ALWAYS)
return
if (!H.can_bite(HH, is_pointblank = 0))
interrupt(INTERRUPT_ALWAYS)
return
if (get_dist(M, HH) > 7)
boutput(M, __red("That target is too far away!"))
return
if (istype(H))
H.vamp_isbiting = HH
HH.vamp_beingbitten = 1
var/obj/projectile/proj = initialize_projectile_ST(HH, new/datum/projectile/special/homing/vamp_blood, M)
var/tries = 10
while (tries > 0 && (!proj || proj.disposed))
proj = initialize_projectile_ST(HH, new/datum/projectile/special/homing/vamp_blood, M)
tries--
proj.special_data["vamp"] = H
proj.special_data["victim"] = HH
proj.targets = list(M)
proj.launch()
if (prob(25))
boutput(HH, __red("Some blood is forced right out of your body!"))
logTheThing("combat", M, HH, "steals blood from %target% at [log_loc(M)].")
onEnd()
..()
if(get_dist(M, HH) > 7 || M == null || HH == null)
interrupt(INTERRUPT_ALWAYS)
return
src.end()
actions.start(new/datum/action/bar/private/icon/vamp_ranged_blood_suc(M,H,HH), M)
onInterrupt() //Called when the action fails / is interrupted.
if (state == ACTIONSTATE_RUNNING)
if (HH.blood_volume < 0)
boutput(M, __red("[HH] doesn't have enough blood left to drink."))
else if (!H.can_take_blood_from(H, HH))
boutput(M, __red("You have drank your fill [HH]'s blood. It tastes all bland and gross now."))
else
boutput(M, __red("Your feast was interrupted."))
if (ability)
ability.doCooldown()
src.end()
..()
proc/end()
if (istype(H))
H.vamp_isbiting = null
if (HH)
HH.vamp_beingbitten = 0 // Victim might have been gibbed, who knowns.
/datum/targetable/vampire/vampire_bite
name = "Bite Victim"
desc = "Bite the victim's neck to drain them of blood."
icon_state = "bite"
targeted = 1
target_nodamage_check = 1
max_range = 1
cooldown = 0
pointCost = 0
when_stunned = 0
not_when_handcuffed = 1
dont_lock_holder = 1
restricted_area_check = 2
var/thrall = 0
cast(mob/target)
if (!holder)
return 1
var/mob/living/M = holder.owner
var/datum/abilityHolder/vampire/H = holder
if (!M || !target || !ismob(target))
return 1
if (get_dist(M, target) > src.max_range)
boutput(M, __red("[target] is too far away."))
return 1
if (actions.hasAction(M, "vamp_blood_suck_ranged"))
boutput(M, __red("You are already performing a Blood action and cannot start a Bite."))
return 1
var/mob/living/carbon/human/HH = target
boutput(M, __blue("You bite [HH] and begin to drain them of blood."))
HH.visible_message("<span style=\"color:red\"><B>[M] bites [HH]!</B></span>")
actions.start(new/datum/action/bar/icon/vamp_blood_suc(M,H,HH,src), M)
return 0
/datum/targetable/vampire/vampire_bite/thrall
thrall = 1
/datum/action/bar/icon/vamp_blood_suc
duration = 30
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
id = "vamp_blood_suck"
icon = 'icons/ui/actions.dmi'
icon_state = "blood"
var/mob/living/carbon/human/M
var/datum/abilityHolder/vampire/H
var/mob/living/carbon/human/HH
var/datum/targetable/vampire/vampire_bite/B
New(user,vampabilityholder,target,biteabil)
M = user
H = vampabilityholder
HH = target
B = biteabil
..()
if (B.thrall)
duration = 60
onUpdate()
..()
if(get_dist(M, HH) > 1 || M == null || HH == null || B == null)
interrupt(INTERRUPT_ALWAYS)
return
onStart()
..()
if(get_dist(M, HH) > 1 || M == null || HH == null || B == null)
interrupt(INTERRUPT_ALWAYS)
return
if (!H.can_bite(HH, is_pointblank = 1))
interrupt(INTERRUPT_ALWAYS)
return
logTheThing("combat", M, HH, "bites %target%'s neck at [log_loc(M)].")
if (istype(H))
H.vamp_isbiting = HH
HH.vamp_beingbitten = 1
onEnd()
..()
if(get_dist(M, HH) > 1 || M == null || HH == null || B == null)
interrupt(INTERRUPT_ALWAYS)
return
if (!H.do_bite(HH,mult = 1.5, thrall = B.thrall))
interrupt(INTERRUPT_ALWAYS)
return
src.end()
actions.start(new/datum/action/bar/icon/vamp_blood_suc(M,H,HH,B), M)
onInterrupt() //Called when the action fails / is interrupted.
if (state == ACTIONSTATE_RUNNING)
if (HH.blood_volume < 0)
boutput(M, __red("[HH] doesn't have enough blood left to drink."))
else if (!H.can_take_blood_from(H, HH))
boutput(M, __red("You have drank your fill [HH]'s blood. It tastes all bland and gross now."))
else
boutput(M, __red("Your feast was interrupted."))
src.end()
..()
proc/end()
if (istype(H))
H.vamp_isbiting = null
if (HH)
HH.vamp_beingbitten = 0 // Victim might have been gibbed, who knowns.