/mob/living/basic/demon/shadow
name = "shadow demon"
desc = "A creature that's barely tangible, you can feel its gaze piercing you."
icon = 'icons/mob/mob.dmi'
icon_state = "shadow_demon"
icon_living = "shadow_demon"
move_resist = MOVE_FORCE_STRONG
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE // so they can tell where the darkness is
see_in_dark = 10 // Below 10 `see_in_dark`, you'll not have full vision with fullscreen
loot = list(/obj/item/organ/internal/heart/demon/shadow)
death_sound = 'sound/shadowdemon/shadowdeath.ogg'
unsuitable_atmos_damage = 18 // You will heal very slowly in a vacuum (2 damage). Go back to the air to heal faster.
var/thrown_alert = FALSE
var/wrapping = FALSE
/// Should only be TRUE if we're shooting the Shadow Grapple right now. If its TRUE, the demon wont be able to shadow crawl.
var/block_shadow_crawl = FALSE
/// Used for fault tolerance. If you set it manually, it may fuck up shooting_grapple var.
var/last_block_shadow_crawl = 0
/mob/living/basic/demon/shadow/Login()
..()
var/list/L = list(
SPAN_DEADSAY("You are a shadow demon!"),
"You are a lethal ambush predator who thrives in the darkness, calling upon the shadows to heal your injured form and increase your speed.",
"Light is however your worst enemy and being exposed for too long will be fatal.",
"Striking your victims with your shadow grapple extinguishes any light sources around them. Striking items silences any light within them.",
"You can wrap your dead victims into a shadow cocoon which provides a shroud of darkness which tears away any light near it.",
"You do not remember anything of your past lives, nor will you remember anything about this one after your death.",
"
[SPAN_MOTD("For more information, check the wiki page: [wiki_link("Shadow_Demon")]")]"
)
to_chat(src, chat_box_red(L.Join("
")))
/mob/living/basic/demon/shadow/Life(seconds, times_fired)
. = ..()
var/lum_count = check_darkness()
var/damage_mod = istype(loc, /obj/effect/dummy/slaughter) ? 0.5 : 1
if(lum_count > 0.2)
adjustBruteLoss(40 * damage_mod) // 10 seconds in light
SEND_SOUND(src, sound('sound/weapons/sear.ogg'))
to_chat(src, SPAN_BIGGERDANGER("The light scalds you!"))
else
adjustBruteLoss(-20)
/mob/living/basic/demon/shadow/UnarmedAttack(atom/A)
// Pick a random attack sound for each attack
attack_sound = pick('sound/shadowdemon/shadowattack2.ogg', 'sound/shadowdemon/shadowattack3.ogg', 'sound/shadowdemon/shadowattack4.ogg')
if(!ishuman(A))
if(isitem(A))
A.extinguish_light()
return ..()
var/mob/living/carbon/human/target = A
if(target.stat != DEAD)
return ..()
if(isLivingSSD(target) && client.send_ssd_warning(target)) // Similar to revenants, only wrap SSD targets if you've accepted the SSD warning
return
if(wrapping)
to_chat(src, SPAN_NOTICE("We are already wrapping something."))
return
visible_message(SPAN_DANGER("[src] begins wrapping [target] in shadowy threads."))
wrapping = TRUE
if(!do_after(src, 4 SECONDS, FALSE, target = target))
wrapping = FALSE
return
target.visible_message(SPAN_WARNING("[src] envelops [target] into an ethereal cocoon, and darkness begins to creep from it."))
var/obj/structure/shadowcocoon/C = new(get_turf(target))
target.extinguish_light() // may as well be safe
target.forceMove(C)
wrapping = FALSE
/mob/living/basic/demon/shadow/proc/block_shadow_crawl()
last_block_shadow_crawl = world.time
block_shadow_crawl = TRUE
addtimer(CALLBACK(src, PROC_REF(check_block_shadow_crawl), last_block_shadow_crawl), 10 SECONDS)
/mob/living/basic/demon/shadow/proc/unblock_shadow_crawl()
last_block_shadow_crawl = 0
block_shadow_crawl = FALSE
/mob/living/basic/demon/shadow/proc/check_block_shadow_crawl(block_time)
if(block_time == last_block_shadow_crawl)
/// it means 10 seconds passed from last shadow grapple shot and it didnt unset block_shadow_crawl
to_chat(src, SPAN_WARNING("You feel good enough to use Shadow Crawl again."))
unblock_shadow_crawl()
/obj/structure/shadowcocoon
name = "shadowy cocoon"
desc = "Something wrapped in what seems to be manifested darkness. Its surface distorts unnaturally, and it emanates deep shadows."
icon = 'icons/effects/effects.dmi'
icon_state = "shadowcocoon"
light_power = -4
light_range = 6
max_integrity = 100
light_color = "#ddd6cf"
anchored = TRUE
/// Amount of SSobj ticks (Roughly 2 seconds) since the last hallucination proc'd
var/time_since_last_hallucination = 0
/// Will we play hallucination sounds or not
var/silent = TRUE
/obj/structure/shadowcocoon/Initialize(mapload)
. = ..()
playsound(loc, 'sound/shadowdemon/shadownode.ogg', 5, TRUE, -1)
START_PROCESSING(SSobj, src)
/obj/structure/shadowcocoon/examine(mob/user)
. = ..()
if(istype(user, /mob/living/basic/demon/shadow))
. += silent ? SPAN_NOTICE("The tendrils are idle and will not produce noise.") : SPAN_NOTICE("The tendrils are agitated and will occasionally produce noise to lure in more prey.")
. += SPAN_NOTICE("Alt+Click to toggle whether [src] should produce noise to lure in victims.")
/obj/structure/shadowcocoon/process()
time_since_last_hallucination++
for(var/atom/to_darken in range(4, src))
if(prob(60) || !length(to_darken.light_sources))
continue
if(iswelder(to_darken) && length(to_darken.light_sources))
var/obj/item/weldingtool/welder_to_darken = to_darken
welder_to_darken.remove_fuel(welder_to_darken.reagents.get_reagent_amount("fuel"))
welder_to_darken.visible_message(SPAN_NOTICE("The shadows swarm around and overwhelm the flame of [welder_to_darken]."))
return
if(istype(to_darken, /obj/item/flashlight/flare))
var/obj/item/flashlight/flare/flare_to_darken = to_darken
if(!flare_to_darken.on)
continue
flare_to_darken.turn_off()
flare_to_darken.fuel = 0
flare_to_darken.visible_message(SPAN_NOTICE("[flare_to_darken] suddenly dims."))
to_darken.extinguish_light()
if(!silent && time_since_last_hallucination >= rand(8, 12))
playsound(src, pick('sound/shadowdemon/shadowhalluc1.ogg', 'sound/shadowdemon/shadowhalluc2.ogg', 'sound/machines/airlock_open.ogg', 'sound/machines/airlock_close.ogg', 'sound/machines/boltsup.ogg', 'sound/shadowdemon/shadowhalluc3.ogg', get_sfx("bodyfall"), 'sound/weapons/egloves.ogg'), 50)
time_since_last_hallucination = 0
// Allows you to turn on cocoons making hallucination sounds or not
/obj/structure/shadowcocoon/AltClick(mob/user)
if(!isdemon(user))
return ..()
if(silent)
to_chat(user, SPAN_NOTICE("You twist and change your trapped victim in [src] to lure in more prey."))
silent = FALSE
return
to_chat(user, SPAN_NOTICE("The tendrils from [src] snap back to their orignal form."))
silent = TRUE
/obj/structure/shadowcocoon/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
if(damage_type != BURN) //I unashamedly stole this from spider cocoon code
return
playsound(loc, 'sound/items/welder.ogg', 100, TRUE)
/obj/structure/shadowcocoon/obj_destruction()
visible_message(SPAN_DANGER("[src] splits open, and the shadows dancing around it fade."))
return ..()
/obj/structure/shadowcocoon/Destroy()
for(var/atom/movable/A in contents)
A.forceMove(loc)
return..()
/mob/living/basic/demon/shadow/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(isliving(AM)) // when a living creature is thrown at it, dont knock it back
return
..()
/mob/living/basic/demon/shadow/Initialize(mapload)
. = ..()
AddSpell(new /datum/spell/fireball/shadow_grapple)
var/datum/spell/bloodcrawl/shadow_crawl/S = new
AddSpell(S)
whisper_action.button_icon_state = "shadow_whisper"
whisper_action.background_icon_state = "shadow_demon_bg"
if(istype(loc, /obj/effect/dummy/slaughter))
S.phased = TRUE
RegisterSignal(loc, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/basic/demon/shadow, check_darkness))
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(check_darkness))
add_overlay(emissive_appearance(icon, "shadow_demon_eye_glow_overlay"))
/mob/living/basic/demon/shadow/proc/check_darkness()
var/turf/T = get_turf(src)
var/lum_count = T.get_lumcount()
if(lum_count > 0.2)
if(!thrown_alert)
thrown_alert = TRUE
throw_alert("light", /atom/movable/screen/alert/lightexposure)
alpha = 255
speed = initial(speed)
else
if(thrown_alert)
thrown_alert = FALSE
clear_alert("light")
alpha = 125
speed = 0.5
return lum_count