mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 18:44:48 +01:00
Refactors revenants to basic mobs (#30661)
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
//Revenants: based off of wraiths from Goon
|
||||
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invsible
|
||||
//Wreck havoc with haunting themed abilities
|
||||
//Admin-spawn or random event
|
||||
|
||||
#define INVISIBILITY_REVENANT 45
|
||||
#define REVENANT_NAME_FILE "revenant_names.json"
|
||||
|
||||
/mob/living/basic/revenant
|
||||
name = "revenant" //The name shown on examine
|
||||
real_name = "revenant" //The name shown in dchat
|
||||
desc = "A malevolent spirit."
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "revenant_idle"
|
||||
mob_biotypes = MOB_SPIRIT
|
||||
sentience_type = SENTIENCE_BOSS // no reviving funny ghost
|
||||
incorporeal_move = INCORPOREAL_MOVE_HOLY_BLOCK
|
||||
see_invisible = INVISIBILITY_REVENANT
|
||||
invisibility = INVISIBILITY_REVENANT
|
||||
health = INFINITY // Revenants don't use health, they use essence instead
|
||||
maxHealth = INFINITY
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
response_help_continuous = "passes through"
|
||||
response_help_simple = "pass through"
|
||||
response_disarm_continuous = "swings at"
|
||||
response_disarm_simple = "swing at"
|
||||
response_harm_continuous = "punches"
|
||||
response_harm_simple = "punch"
|
||||
unsuitable_atmos_damage = 0
|
||||
minimum_survivable_temperature = 0
|
||||
maximum_survivable_temperature = INFINITY
|
||||
harm_intent_damage = 0
|
||||
friendly_verb_continuous = "touches"
|
||||
friendly_verb_simple = "touch"
|
||||
status_flags = 0
|
||||
density = FALSE
|
||||
move_resist = INFINITY
|
||||
mob_size = MOB_SIZE_TINY
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
initial_traits = list(TRAIT_FLYING)
|
||||
contains_xeno_organ = TRUE
|
||||
ignore_generic_organs = TRUE
|
||||
surgery_container = /datum/xenobiology_surgery_container/revenant
|
||||
faction = list("revenant")
|
||||
|
||||
/// The revenant's idle icon
|
||||
var/icon_idle = "revenant_idle"
|
||||
/// The revenant's revealed icon
|
||||
var/icon_reveal = "revenant_revealed"
|
||||
/// The revenant's stunned icon
|
||||
var/icon_stun = "revenant_stun"
|
||||
/// The revant's icon while draining mobs
|
||||
var/icon_drain = "revenant_draining"
|
||||
|
||||
///The resource of revenants. Max health is equal to three times this amount
|
||||
var/essence = 75
|
||||
///The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount.
|
||||
var/essence_regen_cap = 75
|
||||
///If the revenant regenerates essence or not; 1 for yes, 0 for no
|
||||
var/essence_regenerating = TRUE
|
||||
///How much essence regenerates
|
||||
var/essence_regen_amount = 5
|
||||
///How much essence the revenant has stolen
|
||||
var/essence_accumulated = 0
|
||||
///If the revenant can take damage from normal sources.
|
||||
var/revealed = FALSE
|
||||
///How long the revenant is revealed for, is about 2 seconds times this var.
|
||||
var/unreveal_time = 0
|
||||
///How long the revenant is stunned for, is about 2 seconds times this var.
|
||||
var/unstun_time = 0
|
||||
///If the revenant's abilities are blocked by a chaplain's power.
|
||||
var/inhibited = FALSE
|
||||
///How much essence the revenant has drained.
|
||||
var/essence_drained = 0
|
||||
///If the revenant is draining someone.
|
||||
var/draining = FALSE
|
||||
/// contains a list of UIDs of mobs who have been drained. cannot drain the same mob twice.
|
||||
var/list/drained_mobs = list()
|
||||
///How many perfect, regen-cap increasing souls the revenant has.
|
||||
var/perfectsouls = 0
|
||||
/// Are we currently dying? extra check against becomming incorporeal
|
||||
var/dying = FALSE
|
||||
|
||||
/mob/living/basic/revenant/Life(seconds, times_fired)
|
||||
..()
|
||||
if(revealed && essence <= 0)
|
||||
dying = TRUE
|
||||
death()
|
||||
if(essence_regenerating && !inhibited && essence < essence_regen_cap) //While inhibited, essence will not regenerate
|
||||
essence = min(essence_regen_cap, essence+essence_regen_amount)
|
||||
if(unreveal_time && world.time >= unreveal_time && !dying)
|
||||
unreveal_time = 0
|
||||
revealed = FALSE
|
||||
incorporeal_move = INCORPOREAL_MOVE_HOLY_BLOCK
|
||||
invisibility = INVISIBILITY_REVENANT
|
||||
to_chat(src, "<span class='revennotice bold'>You are once more concealed.</span>")
|
||||
if(unstun_time && world.time >= unstun_time && !dying)
|
||||
unstun_time = 0
|
||||
notransform = FALSE
|
||||
to_chat(src, "<span class='revennotice bold'>You can move again!</span>")
|
||||
update_spooky_icon()
|
||||
|
||||
/mob/living/basic/revenant/ex_act(severity)
|
||||
return TRUE //Immune to the effects of explosions.
|
||||
|
||||
/mob/living/basic/revenant/blob_act(obj/structure/blob/B)
|
||||
return //blah blah blobs aren't in tune with the spirit world, or something.
|
||||
|
||||
/mob/living/basic/revenant/singularity_act()
|
||||
return //don't walk into the singularity expecting to find corpses, okay?
|
||||
|
||||
/mob/living/basic/revenant/narsie_act()
|
||||
return //most humans will now be either bones or harvesters, but we're still un-alive.
|
||||
|
||||
/mob/living/basic/revenant/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
|
||||
return FALSE //You are a ghost, atmos and grill makes sparks, and you make your own shocks with lights.
|
||||
|
||||
/mob/living/basic/revenant/adjustHealth(amount, updating_health = TRUE)
|
||||
if(!revealed)
|
||||
return
|
||||
essence = max(0, essence-amount)
|
||||
if(!essence)
|
||||
to_chat(src, "<span class='revendanger'>You feel your essence fraying!</span>")
|
||||
|
||||
/mob/living/basic/revenant/say(message)
|
||||
if(!message)
|
||||
return
|
||||
log_say(message, src)
|
||||
if(copytext(message, 1, 2) == "*")
|
||||
return emote(copytext(message, 2), intentional = TRUE)
|
||||
|
||||
say_dead(message)
|
||||
|
||||
/mob/living/basic/revenant/get_status_tab_items()
|
||||
var/list/status_tab_data = ..()
|
||||
. = status_tab_data
|
||||
status_tab_data[++status_tab_data.len] = list("Current essence:", "[essence]/[essence_regen_cap]E")
|
||||
status_tab_data[++status_tab_data.len] = list("Stolen essence:", "[essence_accumulated]E")
|
||||
status_tab_data[++status_tab_data.len] = list("Stolen perfect souls:", "[perfectsouls]")
|
||||
|
||||
/mob/living/basic/revenant/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
var/list/built_name = list()
|
||||
built_name += pick(strings(REVENANT_NAME_FILE, "spirit_type"))
|
||||
built_name += " of "
|
||||
built_name += pick(strings(REVENANT_NAME_FILE, "adjective"))
|
||||
built_name += pick(strings(REVENANT_NAME_FILE, "theme"))
|
||||
var/combined_name = built_name.Join("")
|
||||
name = combined_name
|
||||
real_name = combined_name
|
||||
|
||||
flags_2 |= RAD_NO_CONTAMINATE_2
|
||||
remove_from_all_data_huds()
|
||||
giveSpells()
|
||||
RegisterSignal(src, COMSIG_BODY_TRANSFER_TO, PROC_REF(make_revenant_antagonist))
|
||||
|
||||
/mob/living/basic/revenant/proc/make_revenant_antagonist(revenant)
|
||||
SIGNAL_HANDLER // COMSIG_BODY_TRANSFER_TO
|
||||
mind.assigned_role = SPECIAL_ROLE_REVENANT
|
||||
mind.special_role = SPECIAL_ROLE_REVENANT
|
||||
giveObjectivesandGoals()
|
||||
|
||||
/mob/living/basic/revenant/proc/giveObjectivesandGoals()
|
||||
if(!mind)
|
||||
return
|
||||
mind.wipe_memory() // someone kill this and give revenants their own minds please
|
||||
SEND_SOUND(src, sound('sound/effects/ghost.ogg'))
|
||||
var/list/messages = list()
|
||||
messages.Add("<span class='deadsay'><font size=3><b>You are a revenant.</b></font></span>")
|
||||
messages.Add("<b>Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.</b>")
|
||||
messages.Add("<b>You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.</b>")
|
||||
messages.Add("<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>")
|
||||
messages.Add("<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>")
|
||||
messages.Add("<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>")
|
||||
messages.Add("<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Revenant)</span>")
|
||||
|
||||
SSticker.mode.traitors |= mind //Necessary for announcing
|
||||
mind.add_mind_objective(/datum/objective/revenant)
|
||||
mind.add_mind_objective(/datum/objective/revenant_fluff)
|
||||
messages.Add(mind.prepare_announce_objectives(FALSE))
|
||||
to_chat(src, chat_box_red(messages.Join("<br>")))
|
||||
|
||||
/mob/living/basic/revenant/proc/giveSpells()
|
||||
AddSpell(new /datum/spell/night_vision/revenant)
|
||||
AddSpell(new /datum/spell/revenant_transmit)
|
||||
AddSpell(new /datum/spell/aoe/revenant/defile)
|
||||
AddSpell(new /datum/spell/aoe/revenant/malfunction)
|
||||
AddSpell(new /datum/spell/aoe/revenant/overload)
|
||||
AddSpell(new /datum/spell/aoe/revenant/haunt_object)
|
||||
AddSpell(new /datum/spell/aoe/revenant/hallucinations)
|
||||
|
||||
|
||||
/mob/living/basic/revenant/dust()
|
||||
return death()
|
||||
|
||||
/mob/living/basic/revenant/gib()
|
||||
return death()
|
||||
|
||||
/mob/living/basic/revenant/death()
|
||||
if(!revealed)
|
||||
return FALSE
|
||||
// Only execute the below if we successfully died
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
to_chat(src, "<span class='revendanger'>NO! No... it's too late, you can feel your essence breaking apart...</span>")
|
||||
notransform = TRUE
|
||||
revealed = TRUE
|
||||
invisibility = 0
|
||||
playsound(src, 'sound/effects/screech.ogg', 100, TRUE)
|
||||
visible_message("<span class='warning'>[src] lets out a waning screech as violet mist swirls around its dissolving body!</span>")
|
||||
icon_state = "revenant_draining"
|
||||
animate(src, alpha = 0, time = 3 SECONDS)
|
||||
visible_message("<span class='danger'>[src]'s body breaks apart into a fine pile of blue dust.</span>")
|
||||
ghostize(GHOST_FLAGS_OBSERVE_ONLY)
|
||||
name = "ectoplasm"
|
||||
desc = "A pile of clumpy dust from a restless spirit"
|
||||
alpha = 255
|
||||
icon_state = "revenant_ectoplasm"
|
||||
move_resist = null
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/revenant/attack_by(obj/item/W, mob/living/user, params)
|
||||
if(..())
|
||||
return FINISH_ATTACK
|
||||
|
||||
if(istype(W, /obj/item/nullrod))
|
||||
visible_message("<span class='warning'>[src] violently flinches!</span>", \
|
||||
"<span class='revendanger'>As \the [W] passes through you, you feel your essence draining away!</span>")
|
||||
adjustBruteLoss(25) //hella effective
|
||||
inhibited = TRUE
|
||||
spawn(30)
|
||||
inhibited = FALSE
|
||||
|
||||
return FINISH_ATTACK
|
||||
|
||||
/mob/living/basic/revenant/proc/castcheck(essence_cost)
|
||||
if(holy_check(src))
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
if(iswallturf(T))
|
||||
to_chat(src, "<span class='revenwarning'>You cannot use abilities from inside of a wall.</span>")
|
||||
return FALSE
|
||||
if(inhibited)
|
||||
to_chat(src, "<span class='revenwarning'>Your powers have been suppressed by nulling energy!</span>")
|
||||
return FALSE
|
||||
if(!change_essence_amount(essence_cost, 1))
|
||||
to_chat(src, "<span class='revenwarning'>You lack the essence to use that ability.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/living/basic/revenant/proc/change_essence_amount(essence_amt, silent = FALSE, source)
|
||||
if(essence + essence_amt <= 0)
|
||||
return
|
||||
essence = max(0, essence + essence_amt)
|
||||
if(essence_amt > 0)
|
||||
essence_accumulated = max(0, essence_accumulated + essence_amt)
|
||||
if(!silent)
|
||||
if(essence_amt > 0)
|
||||
to_chat(src, "<span class='revennotice'>Gained [essence_amt]E from [source].</span>")
|
||||
else
|
||||
to_chat(src, "<span class='revenminor'>Lost [essence_amt]E from [source].</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/basic/revenant/proc/reveal(time)
|
||||
if(time <= 0)
|
||||
return
|
||||
revealed = TRUE
|
||||
invisibility = 0
|
||||
incorporeal_move = NO_INCORPOREAL_MOVE
|
||||
if(!unreveal_time)
|
||||
to_chat(src, "<span class='revendanger'>You have been revealed!</span>")
|
||||
unreveal_time = world.time + time
|
||||
else
|
||||
to_chat(src, "<span class='revenwarning'>You have been revealed!</span>")
|
||||
unreveal_time = unreveal_time + time
|
||||
update_spooky_icon()
|
||||
|
||||
/mob/living/basic/revenant/proc/stun(time)
|
||||
if(time <= 0)
|
||||
return
|
||||
notransform = TRUE
|
||||
if(!unstun_time)
|
||||
to_chat(src, "<span class='revendanger'>You cannot move!</span>")
|
||||
unstun_time = world.time + time
|
||||
else
|
||||
to_chat(src, "<span class='revenwarning'>You cannot move!</span>")
|
||||
unstun_time = unstun_time + time
|
||||
update_spooky_icon()
|
||||
|
||||
/mob/living/basic/revenant/proc/update_spooky_icon()
|
||||
if(dying)
|
||||
return
|
||||
|
||||
if(!revealed)
|
||||
icon_state = icon_idle
|
||||
return
|
||||
|
||||
if(!notransform)
|
||||
icon_state = icon_reveal
|
||||
return
|
||||
|
||||
if(draining)
|
||||
icon_state = icon_drain
|
||||
return
|
||||
|
||||
// No other state is happening, therefore we are stunned
|
||||
icon_state = icon_stun
|
||||
|
||||
|
||||
/datum/objective/revenant
|
||||
needs_target = FALSE
|
||||
var/targetAmount = 100
|
||||
|
||||
/datum/objective/revenant/New()
|
||||
targetAmount = rand(350, 600)
|
||||
explanation_text = "Absorb [targetAmount] points of essence from humans."
|
||||
..()
|
||||
|
||||
/datum/objective/revenant/check_completion()
|
||||
var/total_essence = 0
|
||||
for(var/datum/mind/M in get_owners())
|
||||
if(!istype(M.current, /mob/living/basic/revenant) || QDELETED(M.current))
|
||||
continue
|
||||
var/mob/living/basic/revenant/R = M.current
|
||||
total_essence += R.essence_accumulated
|
||||
if(total_essence < targetAmount)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/revenant_fluff
|
||||
needs_target = FALSE
|
||||
|
||||
/datum/objective/revenant_fluff/New()
|
||||
var/list/explanationTexts = list("Assist and exacerbate existing threats at critical moments.", \
|
||||
"Cause as much chaos and anger as you can without being killed.", \
|
||||
"Damage and render as much of the station rusted and unusable as possible.", \
|
||||
"Disable and cause malfunctions in as many machines as possible.", \
|
||||
"Ensure that any holy weapons are rendered unusable.", \
|
||||
"Hinder the crew while attempting to avoid being noticed.", \
|
||||
"Make the crew as miserable as possible.", \
|
||||
"Make the clown as miserable as possible.", \
|
||||
"Make the captain as miserable as possible.", \
|
||||
"Make the AI as miserable as possible.", \
|
||||
"Annoy the ones that insult you the most.", \
|
||||
"Whisper ghost jokes into peoples heads.", \
|
||||
"Help the crew in critical situations, but take your payments in souls.", \
|
||||
"Prevent the use of energy weapons where possible.")
|
||||
explanation_text = pick(explanationTexts)
|
||||
..()
|
||||
|
||||
/datum/objective/revenant_fluff/check_completion()
|
||||
return TRUE
|
||||
|
||||
// no longer used
|
||||
/obj/item/ectoplasm
|
||||
name = "glimmering residue"
|
||||
desc = "A pile of fine blue dust. Small tendrils of violet mist swirl around it."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "revenantEctoplasm"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
/obj/item/ectoplasm/examine(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='revennotice'>Lifeless ectoplasm, still faintly glimmering in the light. From what was once a spirit seeking revenge on the station.</span>"
|
||||
|
||||
#undef INVISIBILITY_REVENANT
|
||||
#undef REVENANT_NAME_FILE
|
||||
@@ -0,0 +1,533 @@
|
||||
///Harvest
|
||||
/mob/living/basic/revenant/ClickOn(atom/A, params) //Copypaste from ghost code - revenants can't interact with the world directly.
|
||||
|
||||
if(client.click_intercept)
|
||||
client.click_intercept.InterceptClickOn(src, params, A)
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"] && modifiers["shift"] && modifiers["ctrl"])
|
||||
MiddleShiftControlClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"] && modifiers["shift"])
|
||||
MiddleShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"] && modifiers["ctrl"])
|
||||
CtrlShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"] && modifiers["alt"])
|
||||
AltShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"])
|
||||
ShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["alt"])
|
||||
AltClickOn(A)
|
||||
return
|
||||
if(modifiers["ctrl"])
|
||||
CtrlClickOn(A)
|
||||
return
|
||||
|
||||
if(world.time <= next_move)
|
||||
return
|
||||
A.attack_ghost(src)
|
||||
if(ishuman(A) && in_range(src, A))
|
||||
if(isLivingSSD(A) && client.send_ssd_warning(A)) //Do NOT Harvest SSD people unless you accept the warning
|
||||
return
|
||||
Harvest(A)
|
||||
|
||||
/mob/living/basic/revenant/proc/Harvest(mob/living/carbon/human/target)
|
||||
if(!castcheck(0))
|
||||
return
|
||||
if(draining)
|
||||
to_chat(src, "<span class='revenwarning'>You are already siphoning the essence of a soul!</span>")
|
||||
return
|
||||
var/mob_UID = target.UID()
|
||||
if(mob_UID in drained_mobs)
|
||||
to_chat(src, "<span class='revenwarning'>[target]'s soul is dead and empty.</span>")
|
||||
return
|
||||
if(!target.stat)
|
||||
to_chat(src, "<span class='revennotice'>This being's soul is too strong to harvest.</span>")
|
||||
if(prob(10))
|
||||
to_chat(target, "You feel as if you are being watched.")
|
||||
return
|
||||
draining = TRUE
|
||||
essence_drained = rand(15, 20)
|
||||
to_chat(src, "<span class='revennotice'>You search for the soul of [target].</span>")
|
||||
if(do_after(src, 10, 0, target = target)) //did they get deleted in that second?
|
||||
if(target.ckey)
|
||||
to_chat(src, "<span class='revennotice'>Their soul burns with intelligence.</span>")
|
||||
essence_drained += rand(20, 30)
|
||||
if(target.stat != DEAD)
|
||||
to_chat(src, "<span class='revennotice'>Their soul blazes with life!</span>")
|
||||
essence_drained += rand(40, 50)
|
||||
else
|
||||
to_chat(src, "<span class='revennotice'>Their soul is weak and faltering.</span>")
|
||||
if(do_after(src, 20, 0, target = target)) //did they get deleted NOW?
|
||||
switch(essence_drained)
|
||||
if(1 to 30)
|
||||
to_chat(src, "<span class='revennotice'>[target] will not yield much essence. Still, every bit counts.</span>")
|
||||
if(30 to 70)
|
||||
to_chat(src, "<span class='revennotice'>[target] will yield an average amount of essence.</span>")
|
||||
if(70 to 90)
|
||||
to_chat(src, "<span class='revennotice bold'>Such a feast! [target] will yield much essence to you.</span>")
|
||||
if(90 to INFINITY)
|
||||
to_chat(src, "<span class='revenbignotice'>Ah, the perfect soul. [target] will yield massive amounts of essence to you.</span>")
|
||||
if(do_after(src, 20, 0, target = target)) //how about now
|
||||
if(!target.stat)
|
||||
to_chat(src, "<span class='revenwarning'>They are now powerful enough to fight off your draining.</span>")
|
||||
to_chat(target, "<span class='boldannounceic'>You feel something tugging across your body before subsiding.</span>")
|
||||
draining = FALSE
|
||||
return //hey, wait a minute...
|
||||
to_chat(src, "<span class='revenminor'>You begin siphoning essence from [target]'s soul.</span>")
|
||||
if(target.stat != DEAD)
|
||||
to_chat(target, "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>")
|
||||
icon_state = "revenant_draining"
|
||||
reveal(27)
|
||||
stun(27)
|
||||
target.visible_message("<span class='warning'>[target] suddenly rises slightly into the air, [target.p_their()] skin turning an ashy gray.</span>")
|
||||
target.Beam(src,icon_state="drain_life",icon='icons/effects/effects.dmi',time=26)
|
||||
if(do_after(src, 30, 0, target)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
|
||||
change_essence_amount(essence_drained, 0, target)
|
||||
if(essence_drained > 90)
|
||||
essence_regen_cap += 25
|
||||
perfectsouls += 1
|
||||
to_chat(src, "<span class='revennotice bold'>The perfection of [target]'s soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap].</span>")
|
||||
to_chat(src, "<span class='revennotice'>[target]'s soul has been considerably weakened and will yield no more essence for the time being.</span>")
|
||||
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
|
||||
"<span class='revenwarning'>Violets lights, dancing in your vision, getting clo--</span>")
|
||||
drained_mobs.Add(mob_UID)
|
||||
add_attack_logs(src, target, "revenant harvested soul")
|
||||
target.death()
|
||||
else
|
||||
to_chat(src, "<span class='revenwarning'>[target ? "[target] has":"They have"] been drawn out of your grasp. The link has been broken.</span>")
|
||||
draining = 0
|
||||
essence_drained = 0
|
||||
if(target) //Wait, target is WHERE NOW?
|
||||
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
|
||||
"<span class='revenwarning'>Violets lights, dancing in your vision, receding--</span>")
|
||||
return
|
||||
else
|
||||
to_chat(src, "<span class='revenwarning'>You are not close enough to siphon [target ? "[target]'s":"their"] soul. The link has been broken.</span>")
|
||||
draining = FALSE
|
||||
essence_drained = 0
|
||||
return
|
||||
draining = FALSE
|
||||
essence_drained = 0
|
||||
return
|
||||
|
||||
//Toggle night vision: lets the revenant toggle its night vision
|
||||
/datum/spell/night_vision/revenant
|
||||
base_cooldown = 0
|
||||
message = "<span class='revennotice'>You toggle your night vision.</span>"
|
||||
action_icon_state = "r_nightvision"
|
||||
action_background_icon_state = "bg_revenant"
|
||||
|
||||
//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
|
||||
/datum/spell/revenant_transmit
|
||||
name = "Transmit"
|
||||
desc = "Telepathically transmits a message to the target."
|
||||
base_cooldown = 0
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "r_transmit"
|
||||
action_background_icon_state = "bg_revenant"
|
||||
antimagic_flags = MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND
|
||||
|
||||
/datum/spell/revenant_transmit/create_new_targeting()
|
||||
var/datum/spell_targeting/targeted/T = new()
|
||||
T.allowed_type = /mob/living
|
||||
return T
|
||||
|
||||
/datum/spell/revenant_transmit/cast(list/targets, mob/living/basic/revenant/user = usr)
|
||||
for(var/mob/living/M in targets)
|
||||
spawn(0)
|
||||
var/msg = tgui_input_text(user, "What do you wish to tell [M]?", "Transmit")
|
||||
if(!msg)
|
||||
cooldown_handler.revert_cast()
|
||||
return
|
||||
log_say("(REVENANT to [key_name(M)]) [msg]", user)
|
||||
to_chat(user, "<span class='revennotice'><b>You transmit to [M]:</b> [msg]</span>")
|
||||
to_chat(M, "<span class='revennotice'><b>An alien voice resonates from all around...</b></span><i> [msg]</I>")
|
||||
|
||||
/datum/spell/aoe/revenant
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_revenant"
|
||||
/// How long it reveals the revenant in deciseconds
|
||||
var/reveal = 8 SECONDS
|
||||
/// How long it stuns the revenant in deciseconds
|
||||
var/stun = 2 SECONDS
|
||||
/// If it's locked and needs to be unlocked before use
|
||||
var/locked = TRUE
|
||||
/// How much essence it costs to unlock
|
||||
var/unlock_amount = 100
|
||||
/// How much essence it costs to use
|
||||
var/cast_amount = 50
|
||||
antimagic_flags = MAGIC_RESISTANCE_HOLY
|
||||
|
||||
/datum/spell/aoe/revenant/New()
|
||||
..()
|
||||
if(locked)
|
||||
name = "[initial(name)] ([unlock_amount]E)"
|
||||
else
|
||||
name = "[initial(name)] ([cast_amount]E)"
|
||||
action.name = name
|
||||
action.desc = desc
|
||||
action.build_all_button_icons()
|
||||
|
||||
/datum/spell/aoe/revenant/revert_cast(mob/user)
|
||||
. = ..()
|
||||
to_chat(user, "<span class='revennotice'>Your ability wavers and fails!</span>")
|
||||
var/mob/living/basic/revenant/R = user
|
||||
R?.essence += cast_amount //refund the spell and reset
|
||||
|
||||
/datum/spell/aoe/revenant/can_cast(mob/living/basic/revenant/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(user.inhibited)
|
||||
return FALSE
|
||||
if(cooldown_handler.is_on_cooldown())
|
||||
return FALSE
|
||||
if(locked)
|
||||
if(user.essence <= unlock_amount)
|
||||
return FALSE
|
||||
if(user.essence <= cast_amount)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/spell/aoe/revenant/proc/attempt_cast(mob/living/basic/revenant/user = usr)
|
||||
if(locked)
|
||||
if(!user.castcheck(-unlock_amount))
|
||||
cooldown_handler.revert_cast()
|
||||
return FALSE
|
||||
name = "[initial(name)] ([cast_amount]E)"
|
||||
to_chat(user, "<span class='revennotice'>You have unlocked [initial(name)]!</span>")
|
||||
locked = FALSE
|
||||
cooldown_handler.revert_cast()
|
||||
return FALSE
|
||||
if(!user.castcheck(-cast_amount))
|
||||
cooldown_handler.revert_cast()
|
||||
return FALSE
|
||||
name = "[initial(name)] ([cast_amount]E)"
|
||||
user.reveal(reveal)
|
||||
user.stun(stun)
|
||||
if(action)
|
||||
action.build_all_button_icons()
|
||||
return TRUE
|
||||
|
||||
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
|
||||
/datum/spell/aoe/revenant/overload
|
||||
name = "Overload Lights"
|
||||
desc = "Directs a large amount of essence into nearby electrical lights, causing lights to shock those nearby."
|
||||
base_cooldown = 20 SECONDS
|
||||
stun = 3 SECONDS
|
||||
cast_amount = 45
|
||||
var/shock_range = 2
|
||||
var/shock_damage = 40
|
||||
action_icon_state = "overload_lights"
|
||||
aoe_range = 5
|
||||
|
||||
/datum/spell/aoe/revenant/overload/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/targeting = new()
|
||||
targeting.range = aoe_range
|
||||
targeting.allowed_type = /obj/machinery/light
|
||||
return targeting
|
||||
|
||||
/datum/spell/aoe/revenant/overload/cast(list/targets, mob/living/basic/revenant/user = usr)
|
||||
if(attempt_cast(user))
|
||||
for(var/obj/machinery/light/L as anything in targets)
|
||||
INVOKE_ASYNC(src, PROC_REF(shock_lights), L, user)
|
||||
|
||||
/datum/spell/aoe/revenant/overload/proc/shock_lights(obj/machinery/light/L, mob/living/basic/revenant/user)
|
||||
if(!L.on)
|
||||
return
|
||||
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</b></span>")
|
||||
do_sparks(4, 0, L)
|
||||
new /obj/effect/temp_visual/revenant(L.loc)
|
||||
sleep(2 SECONDS)
|
||||
if(!L.on) //wait, wait, don't shock me
|
||||
return
|
||||
flick("[L.base_state]2", L)
|
||||
for(var/mob/living/M in view(shock_range, L))
|
||||
if(M == user)
|
||||
continue
|
||||
M.Beam(L, icon_state = "purple_lightning", icon = 'icons/effects/effects.dmi', time = 0.5 SECONDS)
|
||||
M.electrocute_act(shock_damage, L, flags = SHOCK_NOGLOVES)
|
||||
M.Stun(3 SECONDS)
|
||||
do_sparks(4, 0, M)
|
||||
playsound(M, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
|
||||
|
||||
//Defile: Corrupts nearby stuff, unblesses floor tiles.
|
||||
/datum/spell/aoe/revenant/defile
|
||||
name = "Defile"
|
||||
desc = "Twists and corrupts the nearby area as well as dispelling holy auras on floors. The presence of the living empowers the effects."
|
||||
base_cooldown = 15 SECONDS
|
||||
stun = 1 SECONDS
|
||||
reveal = 4 SECONDS
|
||||
unlock_amount = 75
|
||||
cast_amount = 30
|
||||
action_icon_state = "defile"
|
||||
aoe_range = 4
|
||||
|
||||
/datum/spell/aoe/revenant/defile/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/turf/targeting = new()
|
||||
targeting.range = aoe_range
|
||||
return targeting
|
||||
|
||||
/datum/spell/aoe/revenant/defile/cast(list/targets, mob/living/basic/revenant/user = usr)
|
||||
if(!attempt_cast(user))
|
||||
return
|
||||
for(var/turf/T in targets)
|
||||
T.defile()
|
||||
for(var/atom/A in T.contents)
|
||||
if(istype(A, /obj/structure/window)) // we want to handle glass seperately
|
||||
continue
|
||||
A.defile()
|
||||
for(var/mob/living/carbon/human/living in T.contents)
|
||||
if(!living.mind || living.stat == DEAD) // shouldnt work on dead or mindless mobs
|
||||
continue
|
||||
var/obj/effect/warp_effect/supermatter/warp = new(T)
|
||||
warp.pixel_x += 16
|
||||
warp.pixel_y += 16
|
||||
animate(warp, time = 0.9 SECONDS, transform = matrix().Scale(0.2,0.2))
|
||||
for(var/obj/structure/window/W in range(2, living))
|
||||
W.defile()
|
||||
addtimer(CALLBACK(src, PROC_REF(delete_pulse), warp), 1 SECONDS)
|
||||
|
||||
/datum/spell/aoe/revenant/defile/proc/delete_pulse(warp)
|
||||
qdel(warp)
|
||||
|
||||
//Malfunction: Makes bad stuff happen to robots and machines.
|
||||
/datum/spell/aoe/revenant/malfunction
|
||||
name = "Malfunction"
|
||||
desc = "Corrupts and damages nearby machines and mechanical objects."
|
||||
base_cooldown = 200
|
||||
cast_amount = 45
|
||||
unlock_amount = 150
|
||||
action_icon_state = "malfunction"
|
||||
aoe_range = 2
|
||||
|
||||
/datum/spell/aoe/revenant/malfunction/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/turf/targeting = new()
|
||||
targeting.range = aoe_range
|
||||
return targeting
|
||||
|
||||
//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and gang dominators and everyone will hate you.
|
||||
/datum/spell/aoe/revenant/malfunction/cast(list/targets, mob/living/basic/revenant/user = usr)
|
||||
if(attempt_cast(user))
|
||||
for(var/turf/T in targets)
|
||||
INVOKE_ASYNC(src, PROC_REF(effect), user, T)
|
||||
|
||||
/datum/spell/aoe/revenant/malfunction/proc/effect(mob/living/basic/revenant/user, turf/T)
|
||||
T.rev_malfunction(TRUE)
|
||||
for(var/atom/A in T.contents)
|
||||
A.rev_malfunction(TRUE)
|
||||
|
||||
/**
|
||||
* Makes objects be haunted and then throws them at conscious people to do damage, spooky!
|
||||
*/
|
||||
/datum/spell/aoe/revenant/haunt_object
|
||||
name = "Haunt Objects"
|
||||
desc = "Empower nearby objects to you with ghostly energy, causing them to attack nearby mortals. \
|
||||
Items closer to you are more likely to be haunted."
|
||||
action_icon_state = "haunt"
|
||||
base_cooldown = 60 SECONDS
|
||||
unlock_amount = 150
|
||||
stun = 3 SECONDS
|
||||
reveal = 10 SECONDS
|
||||
/// The maximum number of objects to haunt
|
||||
var/max_targets = 7
|
||||
/// A list of all attack timers started by this spell being cast
|
||||
var/list/attack_timers = list()
|
||||
|
||||
/datum/spell/aoe/revenant/haunt_object/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/targeting = new()
|
||||
targeting.range = aoe_range
|
||||
targeting.allowed_type = /obj/item
|
||||
return targeting
|
||||
|
||||
/datum/spell/aoe/revenant/haunt_object/cast(list/targets, mob/living/basic/revenant/user = usr)
|
||||
if(!attempt_cast(user))
|
||||
return
|
||||
|
||||
var/successes = 0
|
||||
for(var/obj/item/nearby_item as anything in targets)
|
||||
if(successes >= max_targets) // End loop if we've already got 7 spooky items
|
||||
break
|
||||
|
||||
// Don't throw around anchored things or dense things
|
||||
// (Or things not on a turf but I am not sure if range can catch that)
|
||||
if(nearby_item.anchored || nearby_item.density || nearby_item.move_resist == INFINITY || !isturf(nearby_item.loc))
|
||||
continue
|
||||
// Don't throw abstract things
|
||||
if(nearby_item.flags & ABSTRACT)
|
||||
continue
|
||||
// Don't throw things we can't see
|
||||
if(nearby_item.invisibility > user.see_invisible)
|
||||
continue
|
||||
|
||||
var/distance_from_user = max(get_dist(get_turf(nearby_item), get_turf(user)), 1) // get_dist() for same tile dists return -1, we do not want that
|
||||
var/chance_of_haunting = 150 / distance_from_user // The further away things are, the less likely they are to be picked
|
||||
if(!prob(chance_of_haunting))
|
||||
continue
|
||||
|
||||
make_spooky(nearby_item, user)
|
||||
successes++
|
||||
|
||||
if(!successes) //no items to throw
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
// Stop the looping attacks after 65 seconds, roughly 14 attack cycles depending on lag
|
||||
addtimer(CALLBACK(src, PROC_REF(stop_timers)), 65 SECONDS, TIMER_UNIQUE)
|
||||
|
||||
/// Handles making an object haunted and setting it up to attack
|
||||
/datum/spell/aoe/revenant/haunt_object/proc/make_spooky(obj/item/item_to_possess, mob/living/basic/revenant/user)
|
||||
new /obj/effect/temp_visual/revenant(get_turf(item_to_possess)) // Thematic spooky visuals
|
||||
var/mob/living/basic/possessed_object/revenant/possessed_object = new(item_to_possess) // Begin haunting object
|
||||
addtimer(CALLBACK(possessed_object, TYPE_PROC_REF(/mob/living/basic/possessed_object, death)), 70 SECONDS, TIMER_UNIQUE) // De-haunt the object
|
||||
|
||||
/// Stop all attack timers cast by the previous spell use
|
||||
/datum/spell/aoe/revenant/haunt_object/proc/stop_timers()
|
||||
for(var/I in attack_timers)
|
||||
deltimer(I)
|
||||
|
||||
/**
|
||||
* Gives everyone in a 7 tile radius 2 minutes of hallucinations
|
||||
*/
|
||||
/datum/spell/aoe/revenant/hallucinations
|
||||
name = "Hallucination Aura"
|
||||
desc = "Toy with the living nearby, giving them glimpses of things that could be or once were."
|
||||
action_icon_state = "hallucinations"
|
||||
base_cooldown = 15 SECONDS
|
||||
unlock_amount = 50
|
||||
cast_amount = 25
|
||||
stun = 1 SECONDS
|
||||
reveal = 3 SECONDS
|
||||
|
||||
/datum/spell/aoe/revenant/hallucinations/create_new_targeting()
|
||||
var/datum/spell_targeting/aoe/targeting = new()
|
||||
targeting.range = aoe_range
|
||||
targeting.allowed_type = /mob/living/carbon
|
||||
return targeting
|
||||
|
||||
/datum/spell/aoe/revenant/hallucinations/cast(list/targets, mob/living/basic/revenant/user = usr)
|
||||
if(!attempt_cast(user))
|
||||
return
|
||||
|
||||
for(var/mob/living/carbon/M as anything in targets)
|
||||
M.AdjustHallucinate(120 SECONDS, bound_upper = 300 SECONDS) //Lets not let them get more than 5 minutes of hallucinations
|
||||
new /obj/effect/temp_visual/revenant(get_turf(M))
|
||||
|
||||
/// Begin defile and malfunction on-atom definitions
|
||||
|
||||
/atom/proc/defile()
|
||||
return
|
||||
|
||||
/atom/proc/rev_malfunction(cause_emp = TRUE)
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/rev_malfunction(cause_emp = TRUE)
|
||||
to_chat(src, "<span class='warning'>You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")].</span>")
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
if(cause_emp)
|
||||
emp_act(EMP_HEAVY)
|
||||
|
||||
/mob/living/simple_animal/bot/rev_malfunction(cause_emp = TRUE)
|
||||
if(!emagged)
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
locked = FALSE
|
||||
open = TRUE
|
||||
emag_act(usr)
|
||||
|
||||
/obj/rev_malfunction(cause_emp = TRUE)
|
||||
if(prob(20))
|
||||
if(prob(50))
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
emag_act(usr)
|
||||
else if(cause_emp)
|
||||
emp_act(EMP_HEAVY)
|
||||
|
||||
/obj/machinery/clonepod/rev_malfunction(cause_emp = TRUE)
|
||||
..(cause_emp = FALSE)
|
||||
|
||||
/obj/machinery/power/apc/rev_malfunction(cause_emp = TRUE)
|
||||
return
|
||||
|
||||
/obj/machinery/power/smes/rev_malfunction(cause_emp = TRUE)
|
||||
return
|
||||
|
||||
/obj/machinery/computer/emergency_shuttle/rev_malfunction(cause_emp = TRUE)
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/rev_malfunction(cause_emp = TRUE)
|
||||
playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 1)
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
spark_system.start()
|
||||
if(cause_emp)
|
||||
emp_act(EMP_HEAVY)
|
||||
|
||||
/turf/defile()
|
||||
if(flags & BLESSED_TILE)
|
||||
flags &= ~BLESSED_TILE
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
|
||||
/turf/simulated/wall/defile()
|
||||
..()
|
||||
if(prob(15))
|
||||
new/obj/effect/temp_visual/revenant(loc)
|
||||
magic_rust_turf()
|
||||
|
||||
/turf/simulated/wall/indestructible/defile()
|
||||
return
|
||||
|
||||
/turf/simulated/wall/r_wall/defile()
|
||||
..()
|
||||
if(prob(15))
|
||||
new/obj/effect/temp_visual/revenant(loc)
|
||||
magic_rust_turf()
|
||||
|
||||
/mob/living/carbon/human/defile()
|
||||
to_chat(src, "<span class='warning'>You suddenly feel [pick("sick and tired", "tired and confused", "nauseated", "dizzy")].</span>")
|
||||
apply_damage(60, STAMINA)
|
||||
adjustToxLoss(5)
|
||||
AdjustConfused(40 SECONDS, bound_lower = 0, bound_upper = 60 SECONDS)
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
|
||||
/obj/structure/window/defile()
|
||||
take_damage(rand(30,80))
|
||||
if(fulltile)
|
||||
new /obj/effect/temp_visual/revenant/cracks(loc)
|
||||
|
||||
/obj/structure/closet/defile()
|
||||
open()
|
||||
|
||||
/obj/structure/morgue/defile()
|
||||
if(!connected && prob(25))
|
||||
toggle_tray()
|
||||
|
||||
/turf/simulated/floor/defile()
|
||||
..()
|
||||
if(locate(/mob/living/silicon/ai) in src) // Revs don't need a 1-button kill switch on AI cores
|
||||
return
|
||||
if(prob(15))
|
||||
if(intact && floor_tile)
|
||||
new floor_tile(src)
|
||||
broken = FALSE
|
||||
burnt = FALSE
|
||||
make_plating(1)
|
||||
magic_rust_turf()
|
||||
|
||||
/turf/simulated/floor/plating/defile()
|
||||
magic_rust_turf()
|
||||
if(flags & BLESSED_TILE)
|
||||
flags &= ~BLESSED_TILE
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
|
||||
/turf/simulated/floor/engine/cult/defile()
|
||||
if(flags & BLESSED_TILE)
|
||||
flags &= ~BLESSED_TILE
|
||||
new /obj/effect/temp_visual/revenant(loc)
|
||||
|
||||
/obj/machinery/light/defile()
|
||||
flicker(30)
|
||||
Reference in New Issue
Block a user