/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