Adds Dantalion, a new vampire subclass. (#17353)

* dantalion

* my sanity melts

* I am a front end engineer

* first review pass

Co-authored-by: Farie82 <farie82@users.noreply.github.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>

* review pass part 2

* fixes and polishes

* review

* sprites thanks to ramon

* todo and power reshuffle

* styling + fix

* steel review

* thrall commune fixes + polish

* I hate som

* runtime fix + dethrall message

* balance patch + jobban support

* I clicked on the file bundle and this commit took so long to handle

* review

Co-authored-by: Farie82 <farie82@users.noreply.github.com>

* review

Co-authored-by: Farie82 <farie82@users.noreply.github.com>

* "last change they say"

Co-authored-by: Farie82 <farie82@users.noreply.github.com>

* cool animation

* AA review

Co-authored-by: Farie82 <farie82@users.noreply.github.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
This commit is contained in:
Charlie
2022-04-17 20:19:38 +01:00
committed by GitHub
parent 3caf0667a6
commit b48c4e9fe5
15 changed files with 309 additions and 86 deletions
+12 -1
View File
@@ -40,6 +40,16 @@
SSticker.mode.vampire_enthralled -= owner
..()
/datum/antagonist/mindslave/thrall/apply_innate_effects(mob/living/new_body)
..()
var/datum/mind/M = new_body?.mind || owner
M.AddSpell(new /obj/effect/proc_holder/spell/vampire/thrall_commune)
/datum/antagonist/mindslave/thrall/remove_innate_effects(mob/living/old_body)
..()
var/datum/mind/M = old_body?.mind || owner
M.RemoveSpell(/obj/effect/proc_holder/spell/vampire/thrall_commune)
/datum/antagonist/vampire/Destroy(force, ...)
draining = null
QDEL_NULL(subclass)
@@ -57,6 +67,7 @@
if(istype(spell, /datum/vampire_passive))
var/datum/vampire_passive/passive = spell
passive.owner = owner.current
passive.on_apply(src)
powers += spell
owner.current.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
@@ -161,7 +172,7 @@
/datum/antagonist/vampire/proc/check_full_power_upgrade()
if(length(drained_humans) >= FULLPOWER_DRAINED_REQUIREMENT && bloodtotal >= FULLPOWER_BLOODTOTAL_REQUIREMENT)
if(subclass.full_power_overide || (length(drained_humans) >= FULLPOWER_DRAINED_REQUIREMENT && bloodtotal >= FULLPOWER_BLOODTOTAL_REQUIREMENT))
subclass.add_full_power_abilities(src)
@@ -0,0 +1,208 @@
/proc/isvampirethrall(mob/living/M)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/mindslave/thrall)
/datum/vampire_passive/increment_thrall_cap/on_apply(datum/antagonist/vampire/V)
V.subclass.thrall_cap += 1
gain_desc = "You can now thrall one more person, up to a maximum of [V.subclass.thrall_cap]"
/datum/vampire_passive/increment_thrall_cap/two
/datum/vampire_passive/increment_thrall_cap/three
/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
/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/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
var/mob/living/target = targets[1]
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, 15 SECONDS))
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 moved.</span>")
/obj/effect/proc_holder/spell/vampire/enthrall/proc/can_enthrall(mob/living/user, mob/living/carbon/C)
if(!C)
log_runtime(EXCEPTION("target was null while trying to vampire enthrall, attacker is [user] [user.key] \ref[user]"), user)
return FALSE
if(!user.mind.som)
CRASH("Dantalion Thrall datum ended up null.")
if(!ishuman(C))
to_chat(user, "<span class='warning'>You can only enthrall sentient humanoids!</span>")
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
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire)
if(V.subclass.thrall_cap <= length(user.mind.som.serv))
to_chat(user, "<span class='warning'>You don't have enough power to enthrall any more people!</span>")
return FALSE
if(ismindshielded(C) || C.mind.has_antag_datum(/datum/antagonist/vampire) || C.mind.has_antag_datum(/datum/antagonist/mindslave))
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.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>")
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 FALSE
var/greet_text = "<b>You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.</b>"
H.mind.add_antag_datum(new /datum/antagonist/mindslave/thrall(user.mind, greet_text))
if(jobban_isbanned(H, ROLE_VAMPIRE))
SSticker.mode.replace_jobbanned_player(H, SPECIAL_ROLE_VAMPIRE_THRALL)
H.Stun(2)
user.create_log(CONVERSION_LOG, "vampire enthralled", H)
H.create_log(CONVERSION_LOG, "was vampire enthralled", user)
/obj/effect/proc_holder/spell/vampire/thrall_commune
name = "Commune"
desc = "Talk to your thralls telepathically."
gain_desc = "You have gained the ability to commune with your thralls."
action_icon_state = "vamp_communication"
charge_max = 2 SECONDS
/obj/effect/proc_holder/spell/vampire/thrall_commune/create_new_handler() //so thralls can use it
return
/datum/spell_targeting/select_vampire_network/choose_targets(mob/user, obj/effect/proc_holder/spell/spell, params, atom/clicked_atom) // Returns the vampire and their thralls. If user is a thrall then it will look up their master's network
var/list/mob/living/targets = list()
var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) // if the user is a vampire
if(!V)
for(var/datum/mind/M in user.mind.som.masters) // if the user is a thrall
V = M.has_antag_datum(/datum/antagonist/vampire)
if(V)
break
if(!V)
return
if(!V.owner.som) // I hate som
stack_trace("Dantalion Thrall datum ended up null.")
return
for(var/datum/mind/thrall in V.owner.som.serv)
targets += thrall.current
targets += V.owner.current
return targets
/obj/effect/proc_holder/spell/vampire/thrall_commune/create_new_targeting()
var/datum/spell_targeting/select_vampire_network/T = new
return T
/obj/effect/proc_holder/spell/vampire/thrall_commune/cast(list/targets, mob/user)
var/input = stripped_input(user, "Enter a message to relay to the other thralls", "Thrall Commune", "")
if(!input)
revert_cast(user)
return
var/title = isvampirethrall(user) ? "Thrall" : "<b>Vampire Master</b>" // if admins give this to a non vampire/thrall it is not my problem
var/message = "[user.real_name] ([title]): [input]"
for(var/mob/M in targets)
to_chat(M, "<span class='shadowling'>[message]</span>")
for(var/mob/M in GLOB.dead_mob_list)
to_chat(M, "<span class='shadowling'>([ghost_follow_link(user, ghost=M)]): [message] </span>")
log_say("(DANTALION) [input]", user)
user.create_log(SAY_LOG, "(DANTALION) [input]")
/obj/effect/proc_holder/spell/vampire/pacify
name = "Pacify (10)"
desc = "Pacify a target temporarily, making them unable to cause harm."
gain_desc = "You have gained the ability to pacify someone's harmful tendencies, preventing them from doing any physical harm to anyone."
action_icon_state = "pacify"
charge_max = 30 SECONDS
required_blood = 10
/obj/effect/proc_holder/spell/vampire/pacify/create_new_targeting()
var/datum/spell_targeting/click/T = new
T.range = 7
T.click_radius = 1
T.allowed_type = /mob/living/carbon/human
return T
/obj/effect/proc_holder/spell/vampire/pacify/cast(list/targets, mob/user)
for(var/mob/living/carbon/human/H as anything in targets)
H.apply_status_effect(STATUS_EFFECT_PACIFIED)
/obj/effect/proc_holder/spell/vampire/self/decoy
name = "Deploy Decoy (30)"
desc = "Briefly turn invisible and deploy a decoy illusion to fool your prey."
gain_desc = "You have gained the ability to turn invisible and create decoy illusions."
action_icon_state = "decoy"
required_blood = 30
charge_max = 40 SECONDS
/obj/effect/proc_holder/spell/vampire/self/decoy/cast(list/targets, mob/user)
var/mob/living/simple_animal/hostile/illusion/escape/E = new(get_turf(user))
E.Copy_Parent(user, 20, 20)
E.GiveTarget(user) //so it starts running right away
E.Goto(user, E.move_to_delay, E.minimum_distance)
user.make_invisible()
addtimer(CALLBACK(user, /mob/living/.proc/reset_visibility), 6 SECONDS)
/obj/effect/proc_holder/spell/vampire/rally_thralls
name = "Rally Thralls (100)"
desc = "Removes all incapacitating effects from your nearby thralls."
gain_desc = "You have gained the ability to remove all incapacitating effects from nearby thralls."
action_icon_state = "thralls_up"
required_blood = 100
charge_max = 100 SECONDS
/obj/effect/proc_holder/spell/vampire/rally_thralls/create_new_targeting()
var/datum/spell_targeting/aoe/thralls/A = new
A.allowed_type = /mob/living/carbon/human
A.range = 7
return A
/datum/spell_targeting/aoe/thralls/valid_target(target, user, obj/effect/proc_holder/spell/spell, check_if_in_range)
if(!isvampirethrall(target))
return FALSE
return ..()
/obj/effect/proc_holder/spell/vampire/rally_thralls/cast(list/targets, mob/user)
for(var/mob/living/carbon/human/H as anything in targets)
var/image/I = image('icons/effects/vampire_effects.dmi', "rallyoverlay", layer = EFFECTS_LAYER)
playsound(H, 'sound/magic/staff_healing.ogg', 30)
H.remove_CC()
H.add_overlay(I)
addtimer(CALLBACK(H, /atom/.proc/cut_overlay, I), 6 SECONDS) // this makes it obvious who your thralls are for a while.
/obj/effect/proc_holder/spell/vampire/hysteria
name = "Mass Hysteria (70)"
desc = "Casts a powerful illusion to make everyone nearby percieve others to looks like random animals after briefly blinding them."
gain_desc = "You have gained the ability to make everyone nearby percieve others to looks like random animals after briefly blinding them."
action_icon_state = "hysteria"
required_blood = 70
charge_max = 180 SECONDS
/obj/effect/proc_holder/spell/vampire/hysteria/create_new_targeting()
var/datum/spell_targeting/aoe/A = new
A.range = 8
A.allowed_type = /mob/living/carbon/human
return A
/obj/effect/proc_holder/spell/vampire/hysteria/cast(list/targets, mob/user)
for(var/mob/living/carbon/human/H as anything in targets)
if(!H.affects_vampire(user))
continue
H.flash_eyes(1, TRUE) // flash to give them a second to lose track of who is who
new /obj/effect/hallucination/delusion/long(get_turf(user), H)
@@ -1,7 +1,7 @@
//This should hold all the vampire related powers
/mob/living/proc/affects_vampire(mob/user)
//Other vampires aren't affected
if(mind?.has_antag_datum(/datum/antagonist/vampire))
//Other vampires and thralls aren't affected
if(mind?.has_antag_datum(/datum/antagonist/vampire) || mind?.has_antag_datum(/datum/antagonist/mindslave/thrall))
return FALSE
//Vampires who have reached their full potential can affect nearly everything
var/datum/antagonist/vampire/V = user?.mind.has_antag_datum(/datum/antagonist/vampire)
@@ -44,6 +44,9 @@
owner = null
return ..()
/datum/vampire_passive/proc/on_apply(datum/antagonist/vampire/V)
return
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
name = "Rejuvenate"
desc = "Use reserve blood to enliven your body, removing any incapacitating effects."
@@ -102,7 +105,7 @@
/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 = new(user, src, ui_key, "SpecMenu", "Specialisation Menu", 1200, 600, master_ui, state)
ui.set_autoupdate(FALSE)
ui.open()
@@ -134,6 +137,10 @@
vamp.add_subclass(SUBCLASS_GARGANTUA)
vamp.upgrade_tiers -= type
vamp.remove_ability(src)
if("dantalion")
vamp.add_subclass(SUBCLASS_DANTALION)
vamp.upgrade_tiers -= type
vamp.remove_ability(src)
/datum/antagonist/vampire/proc/add_subclass(subclass_to_add, announce = TRUE)
@@ -340,78 +347,3 @@
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 && M.mind.has_antag_datum(/datum/antagonist/mindslave/thrall)
/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/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
for(var/mob/living/target in targets)
user.visible_message("<span class='warning'>[user] bites [target]'s neck!</span>", "<span class='warning'>You bite [target]'s neck and begin the flow of power.</span>")
to_chat(target, "<span class='warning'>You feel the tendrils of evil invade your mind.</span>")
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.has_antag_datum(/datum/antagonist/vampire)) || (isvampirethrall(C)))
C.visible_message("<span class='warning'>[C] seems to resist the takeover!</span>", "<span class='notice'>You feel a familiar sensation in your skull that quickly dissipates.</span>")
return FALSE
if(!C.affects_vampire(user))
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 FALSE
var/greet_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.add_antag_datum(new /datum/antagonist/mindslave/thrall(user.mind, greet_text))
H.Stun(2)
add_attack_logs(user, H, "Vampire-thralled")
@@ -7,6 +7,9 @@
var/list/fully_powered_abilities
/// Whether or not a vampire heals more based on damage taken.
var/improved_rejuv_healing = FALSE
/// maximun number of thralls a vampire may have at a time. incremented as they grow stronger, up to a cap at full power.
var/thrall_cap = 1
var/full_power_overide = FALSE
/datum/vampire_subclass/proc/add_subclass_ability(datum/antagonist/vampire/vamp)
for(var/thing in standard_powers)
@@ -46,25 +49,45 @@
/obj/effect/proc_holder/spell/vampire/charge)
improved_rejuv_healing = TRUE
/datum/vampire_subclass/dantalion
name = "dantalion"
standard_powers = list(/obj/effect/proc_holder/spell/vampire/enthrall = 150,
/obj/effect/proc_holder/spell/vampire/thrall_commune = 150,
/obj/effect/proc_holder/spell/vampire/pacify = 250,
/obj/effect/proc_holder/spell/vampire/self/decoy = 400,
/datum/vampire_passive/increment_thrall_cap = 400,
/obj/effect/proc_holder/spell/vampire/rally_thralls = 600,
/datum/vampire_passive/increment_thrall_cap/two = 600)
fully_powered_abilities = list(/datum/vampire_passive/full,
/obj/effect/proc_holder/spell/vampire/hysteria,
/datum/vampire_passive/increment_thrall_cap/three)
/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/enthrall,
/obj/effect/proc_holder/spell/vampire/thrall_commune,
/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/vampire/pacify,
/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/self/decoy,
/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,
/obj/effect/proc_holder/spell/vampire/rally_thralls,
/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,
/obj/effect/proc_holder/spell/vampire/hysteria,
/obj/effect/proc_holder/spell/vampire/raise_vampires,
/datum/vampire_passive/xray)
improved_rejuv_healing = TRUE
thrall_cap = 150 // can thrall high pop
@@ -471,6 +471,9 @@
I.override = TRUE
add_icon(I)
/obj/effect/hallucination/delusion/long
duration = 30 SECONDS
/**
* Returns the image to use as override to the target's appearance.
*/
@@ -449,7 +449,7 @@
if((NO_BLOOD in target.dna.species.species_traits) || target.dna.species.exotic_blood || !target.blood_volume)
to_chat(user, "<span class='warning'>They have no blood!</span>")
return
if(target.mind && target.mind.has_antag_datum(/datum/antagonist/vampire))
if(target.mind && (target.mind.has_antag_datum(/datum/antagonist/vampire) || target.mind.has_antag_datum(/datum/antagonist/mindslave/thrall)))
to_chat(user, "<span class='warning'>Your fangs fail to pierce [target.name]'s cold flesh</span>")
return
if(HAS_TRAIT(target, TRAIT_SKELETONIZED))
@@ -251,6 +251,7 @@
if(isvampirethrall(M))
M.mind.remove_antag_datum(/datum/antagonist/mindslave/thrall)
holder.remove_reagent(id, volume)
M.visible_message("<span class='biggerdanger'>[M] recoils, their skin flushes with colour, regaining their sense of control!</span>")
M.SetJitter(0)
M.SetStuttering(0)
M.SetConfused(0)