diff --git a/modular_ss220/_defines220/code/antagonists.dm b/modular_ss220/_defines220/code/antagonists.dm
index 9ec47589ae1a..d965d27e6c72 100644
--- a/modular_ss220/_defines220/code/antagonists.dm
+++ b/modular_ss220/_defines220/code/antagonists.dm
@@ -1,5 +1,6 @@
#define ROLE_BLOOD_BROTHER "blood brother"
#define ROLE_VOX_RAIDER "vox raider"
+#define ROLE_SHADOWLING "shadowling"
#define VOX_RAID_FREQ 1220
diff --git a/modular_ss220/_defines220/code/gamemode.dm b/modular_ss220/_defines220/code/gamemode.dm
index 85e57c57aa58..6fad373f856d 100644
--- a/modular_ss220/_defines220/code/gamemode.dm
+++ b/modular_ss220/_defines220/code/gamemode.dm
@@ -1,5 +1,8 @@
#define SPECIAL_ROLE_BLOOD_BROTHER "Blood Brother"
#define SPECIAL_ROLE_VOX_RAIDER "Vox Raider"
+#define SPECIAL_ROLE_SHADOWLING "Shadowling"
+#define SPECIAL_ROLE_SHADOWLING_THRALL "Shadowling Thrall"
+#define SPECIAL_ROLE_SHADOW_FATHER "Shadow Father"
#define isvoxcash(W) (istype(W, /obj/item/stack/vox_cash))
diff --git a/modular_ss220/_defines220/code/species.dm b/modular_ss220/_defines220/code/species.dm
index 611dbc273878..32bee4f27123 100644
--- a/modular_ss220/_defines220/code/species.dm
+++ b/modular_ss220/_defines220/code/species.dm
@@ -1,5 +1,8 @@
#define isnucleation(A) (is_species(A, /datum/species/nucleation))
+#define isshadowling(A) (is_species(A, /datum/species/shadow/ling))
+#define isshadowlinglesser(A) (is_species(A, /datum/species/shadow/ling/lesser))
+
//MATERIAL CLASS FOR RACE EAT
#define MATERIAL_CLASS_NONE 0
#define MATERIAL_CLASS_CLOTH 1
diff --git a/modular_ss220/antagonists/_antagonists.dm b/modular_ss220/antagonists/_antagonists.dm
index ea08e2e1f0c0..b8a8944d8539 100644
--- a/modular_ss220/antagonists/_antagonists.dm
+++ b/modular_ss220/antagonists/_antagonists.dm
@@ -19,6 +19,9 @@
GLOB.huds += new/datum/atom_hud/antag()
GLOB.special_roles |= ROLE_VOX_RAIDER
+ GLOB.huds += new/datum/atom_hud/antag()
+ GLOB.special_roles |= ROLE_SHADOWLING
+
SSradio.ANTAG_FREQS |= list(VOX_RAID_FREQ)
SSradio.radiochannels |= list(
diff --git a/modular_ss220/antagonists/_antagonists.dme b/modular_ss220/antagonists/_antagonists.dme
index 95aa531b404b..832a1c5d1e2f 100644
--- a/modular_ss220/antagonists/_antagonists.dme
+++ b/modular_ss220/antagonists/_antagonists.dme
@@ -8,3 +8,24 @@
#include "code/configuration/antag_mix_configuration.dm"
#include "code/mind/memory_edit.dm"
#include "code/antag_mix/antag_mix.dm"
+#include "code/shadowlings/shadowling_datum.dm"
+#include "code/shadowlings/shadowling_species.dm"
+#include "code/shadowlings/shadowling_language.dm"
+#include "code/shadowlings/shadowling_eyes.dm"
+#include "code/shadowlings/spells/shadowling_spell.dm"
+#include "code/shadowlings/spells/shadowling_glare.dm"
+#include "code/shadowlings/spells/shadowling_shadow_walk.dm"
+#include "code/shadowlings/spells/shadowling_icy_veins.dm"
+#include "code/shadowlings/spells/shadowling_screech.dm"
+#include "code/shadowlings/spells/shadowling_blindness_smoke.dm"
+#include "code/shadowlings/spells/shadowling_veil.dm"
+#include "code/shadowlings/spells/shadowling_enthrall.dm"
+#include "code/shadowlings/spells/shadowling_tear_the_reality.dm"
+#include "code/shadowlings/spells/shadowling_rift_in.dm"
+#include "code/shadowlings/spells/shadow_father_crawl.dm"
+#include "code/shadowlings/shadowling_clothes.dm"
+#include "code/shadowlings/spells/shadowling_hatch.dm"
+#include "code/shadowlings/father_of_shadows.dm"
+#include "code/shadowlings/shadowling_traps.dm"
+#include "code/shadowlings/shadowling_gamemode.dm"
+#include "code/shadowlings/shadowling_mob.dm"
diff --git a/modular_ss220/antagonists/code/shadowlings/father_of_shadows.dm b/modular_ss220/antagonists/code/shadowlings/father_of_shadows.dm
new file mode 100644
index 000000000000..b39c6de20569
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/father_of_shadows.dm
@@ -0,0 +1,164 @@
+#define HEALTH_DECREASING_AMOUNT 70
+#define MAX_CONSUMED_CORPSES 4
+
+/mob/living/simple_animal/demon/shadow_father // Can't inherit from demon/shadow because of 'initialize' proc
+ name = "отец теней"
+ desc = "Крупное демоническое существо, сотканное из теней. Из его красных глаз сочится потусторонняя энергия."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/father_of_shadows.dmi'
+ icon_state = "father_of_shadows"
+ icon_living = "father_of_shadows"
+ a_intent = INTENT_HARM
+ mob_biotypes = MOB_ORGANIC | MOB_HUMANOID
+ stop_automated_movement = TRUE
+ status_flags = CANPUSH
+ attack_sound = 'sound/misc/demon_attack1.ogg'
+ death_sound = 'sound/misc/demon_dies.ogg'
+ 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)
+ minbodytemp = 0
+ maxbodytemp = INFINITY
+ faction = list("demon")
+ move_resist = MOVE_FORCE_STRONG
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
+ see_in_dark = 10
+ death_sound = 'sound/shadowdemon/shadowdeath.ogg'
+ speed = 1
+ maxHealth = 350
+ health = 350
+ /// Used to check light exposure alert state
+ var/thrown_alert
+ /// Contain list of all traps placed by this shadow father
+ var/list/obj/structure/shadow_trap/placed_traps = list()
+ /// Used to check state in consumption logic
+ var/is_consuming = FALSE
+ /// Amount of bodies, consumed by the shadow father
+ var/consumed = 0
+ /// Used to initialize hud
+ var/mind_initialized = FALSE
+
+/mob/living/simple_animal/demon/shadow_father/Login()
+ . = ..()
+ var/list/L = list(
+ "Вы Отец Тьмы!",
+ "Вы коварный и подлый антагонист, который должен подготовить эту станцию для прихода своих детей.",
+ "Свет - ваш худший враг, но вы не получаете от него урона, находясь в астральной форме.",
+ "Подготавливайте ловушки и засады в технических туннелях, после чего заманивайте в них жертв.",
+ "Погружайте в тень тела погибших членов экипажа, чтобы, в будущем, превратить их в своих детей.",
+ "Накопив достаточно погружённых, издайте свой последний вопль и призовите на станцию тенелингов.",
+ "",
+ "
Для подробной информации, посетите вики: [wiki_link("Shadowlings")]"
+ )
+ to_chat(src, chat_box_red(L.Join("
")))
+ if(!mind || mind_initialized)
+ return
+ mind_initialized = TRUE
+ var/datum/atom_hud/hud = GLOB.huds[ANTAG_HUD_SHADOW]
+ hud.add_hud_to(src)
+ mind.add_antag_datum(/datum/antagonist/shadow_father)
+
+/mob/living/simple_animal/demon/shadow_father/Initialize(mapload)
+ . = ..()
+ add_overlay(emissive_appearance(icon, "father_of_shadows_eye_glow_overlay"))
+ grant_spells()
+
+/mob/living/simple_animal/demon/shadow_father/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, "The light scalds you!")
+ else
+ adjustBruteLoss(-20)
+
+/mob/living/simple_animal/demon/shadow_father/death(gibbed)
+ for(var/trap in placed_traps)
+ qdel(trap)
+ INVOKE_ASYNC(SSticker.mode, TYPE_PROC_REF(/datum/game_mode, begin_shadowling_invasion), src, FALSE)
+ return ..(gibbed)
+
+/mob/living/simple_animal/demon/shadow_father/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
+
+/mob/living/simple_animal/demon/shadow_father/proc/grant_spells()
+ whisper_action.button_overlay_icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_actions.dmi'
+ whisper_action.button_overlay_icon_state = "father_whisper"
+ whisper_action.button_background_icon_state = "shadow_demon_bg"
+
+ var/list/datum/spell/spells_to_grant = list(
+ new /datum/spell/bloodcrawl/shadow_crawl/father,
+ new /datum/spell/shadowling/veil,
+ new /datum/spell/shadowling/screech,
+ new /datum/spell/shadowling/glare,
+ new /datum/spell/shadowling/self/place_trap/stun,
+ new /datum/spell/shadowling/self/place_trap/poison,
+ new /datum/spell/shadowling/self/place_trap/blindness,
+ new /datum/spell/shadowling/self/tear_the_reality,
+ )
+ for(var/datum/spell/spell in spells_to_grant)
+ AddSpell(spell)
+
+/mob/living/simple_animal/demon/shadow_father/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/simple_animal/demon/shadow_father/UnarmedAttack(atom/A)
+ // Prevent self-attack
+ if(A == src)
+ return
+ // 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) || isnull(target.mind))
+ return
+
+ if(is_consuming)
+ to_chat(src, span_notice("Мы уже поглощаем кого-то."))
+ return
+
+ if(consumed == MAX_CONSUMED_CORPSES)
+ return
+
+ visible_message(span_danger("[src] начинает окутывать [target] теневой пеленой!"))
+ is_consuming = TRUE
+ playsound(src, 'modular_ss220/antagonists/sound/shadowlings/shadow_consumption_start.ogg', 50, TRUE)
+ if(!do_after(src, 15 SECONDS, FALSE, target = target))
+ is_consuming = FALSE
+ return
+
+ target.visible_message(span_danger("[src] полностью окутывает [target] тенями и тело исчезает в потусторонней пелене! [src], похоже, стал слабее!"))
+ qdel(target)
+ playsound(src, 'modular_ss220/antagonists/sound/shadowlings/shadow_consumption_end.ogg', 50, TRUE)
+ maxHealth -= HEALTH_DECREASING_AMOUNT
+ health = clamp(health, 0, maxHealth)
+ consumed++
+ if(consumed < MAX_CONSUMED_CORPSES)
+ to_chat(src, span_purple("Мы успешно погрузили тело жервты во тьму. Мы слабеем, но теперь после нашей смерти в мир явится больше тенелингов. Мы можем поглотить ещё [MAX_CONSUMED_CORPSES - consumed] тел."))
+ else
+ to_chat(src, span_purple("Это была последняя жертва. Всё что мы теперь можем сделать, это начать прорыв завесы."))
+ is_consuming = FALSE
+
+#undef HEALTH_DECREASING_AMOUNT
+#undef MAX_CONSUMED_CORPSES
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_clothes.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_clothes.dm
new file mode 100644
index 000000000000..7bc1230f1b98
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_clothes.dm
@@ -0,0 +1,81 @@
+/obj/item/clothing/under/shadowling
+ name = "почерневшая плоть"
+ desc = "Чёрная хитинистая кожа с выступающими красными венами."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_uniform"
+ origin_tech = null
+ flags = NODROP | DROPDEL
+ has_sensor = FALSE
+ displays_id = FALSE
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+
+
+/obj/item/clothing/suit/space/shadowling
+ name = "теневой панцирь"
+ desc = "Тёмный полупросрачный панцирь, защищающий тенелинга от урона и немного от космоса." //Still takes damage from spacewalking but is immune to space itself
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_suit"
+ icon_override = 'icons/mob/clothing/underwear.dmi' // It's required to avoid 'suit' test icon
+ body_parts_covered = FULL_BODY //Shadowlings are immune to space
+ cold_protection = FULL_BODY
+ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ flags_inv = HIDEGLOVES | HIDESHOES | HIDEJUMPSUIT
+ flags = NODROP | DROPDEL
+ slowdown = 0
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ heat_protection = null //You didn't expect a light-sensitive creature to have heat resistance, did you?
+ max_heat_protection_temperature = null
+ armor = list(melee = 25, bullet = 25, laser = 0, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 100, acid = 100)
+
+
+/obj/item/clothing/shoes/shadowling
+ name = "теневые крюки"
+ desc = "Угольно-чёрные крюки, расположенные на ногах тенелингов."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_shoes"
+ resistance_flags = LAVA_PROOF|FIRE_PROOF|ACID_PROOF
+ flags = NODROP | DROPDEL
+ no_slip = TRUE
+
+/obj/item/clothing/mask/gas/shadowling
+ name = "теневая маска"
+ desc = "Тёмная хитиновая маска, располагающаяся на лицах тенелингов."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_mask"
+ siemens_coefficient = 0
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ flags_cover = MASKCOVERSEYES
+ flags = NODROP | DROPDEL
+
+/obj/item/clothing/gloves/shadowling
+ name = "теневые наручи"
+ desc = "Плотной слой теневой материи, защищающий руки тенелинга от и электричества."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_gloves"
+ siemens_coefficient = 0
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ flags = NODROP | DROPDEL
+
+/obj/item/clothing/head/shadowling
+ name = "теневой шлем"
+ desc = "Шлемообразный защитный панцирь, расположенный на голове тенелинга."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_helmet"
+ cold_protection = HEAD
+ min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
+ heat_protection = HEAD
+ max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ flags_cover = HEADCOVERSEYES //We don't need to cover mouth
+ flags = NODROP | DROPDEL | BLOCKHAIR
+
+/obj/item/clothing/glasses/shadowling
+ name = "багровые линзы"
+ desc = "Небольшие красные мембраны, защищающие уязвимые глаза тенелингов."
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi'
+ icon_state = "shadowling_glasses"
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ flash_protect = FLASH_PROTECTION_SENSITIVE
+ vision_flags = SEE_MOBS
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ flags = NODROP | DROPDEL
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_datum.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_datum.dm
new file mode 100644
index 000000000000..b7126c42c722
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_datum.dm
@@ -0,0 +1,32 @@
+/datum/antagonist/shadowling
+ name = "Shadowling"
+ antag_hud_type = ANTAG_HUD_SHADOW
+ antag_hud_name = "hudshadowling"
+ job_rank = ROLE_SHADOWLING
+ special_role = SPECIAL_ROLE_SHADOWLING
+ wiki_page_name = "Shadowlings"
+
+/datum/antagonist/shadow_father
+ name = "Shadow Father"
+ antag_hud_type = ANTAG_HUD_SHADOW
+ antag_hud_name = "hudshadowfather"
+ job_rank = ROLE_SHADOWLING // I don't change it, so there is no different database line for shadowling roles
+ special_role = SPECIAL_ROLE_SHADOW_FATHER
+ wiki_page_name = "Shadowlings"
+
+/datum/antagonist/shadowling_thrall
+ name = "Shadowling Thrall"
+ antag_hud_type = ANTAG_HUD_SHADOW
+ antag_hud_name = "hudshadowlingthrall"
+ job_rank = ROLE_SHADOWLING // I don't change it, so there is no different database line for shadowling roles
+ special_role = SPECIAL_ROLE_SHADOWLING_THRALL
+ wiki_page_name = "Shadowlings"
+
+/proc/is_thrall(var/mob/living/M)
+ return TRUE
+
+/proc/is_shadow_or_thrall(var/mob/living/M)
+ return TRUE
+
+/proc/is_shadow(var/mob/living/M)
+ return TRUE
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_eyes.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_eyes.dm
new file mode 100644
index 000000000000..89dae4a4c1e4
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_eyes.dm
@@ -0,0 +1,6 @@
+// Changing the organ usage action background
+/obj/item/organ/internal/eyes/night_vision/nightmare
+ actions_types = list(/datum/action/item_action/organ_action/use/shadowling)
+
+/datum/action/item_action/organ_action/use/shadowling
+ button_background_icon_state = "shadow_demon_bg"
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_gamemode.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_gamemode.dm
new file mode 100644
index 000000000000..8394f1e81533
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_gamemode.dm
@@ -0,0 +1,31 @@
+/datum/game_mode
+ var/list/datum/mind/shadowlings = list()
+ var/list/datum/mind/shadowling_thralls = list()
+ var/list/datum/mind/shadow_fathers = list()
+ var/victory_warning_announced = FALSE
+
+/// Called when shadow father is killed or when special spell is used
+/datum/game_mode/proc/begin_shadowling_invasion(mob/living/simple_animal/demon/shadow_father/father, spell_used)
+ // Spawning father as shadowling
+ if(spell_used)
+ var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(father.loc)
+ var/mob/living/carbon/human/shadowling/ling = new /mob/living/carbon/human/shadowling(holder)
+ ling.AddSpell(new /datum/spell/shadowling/self/rift_in)
+ ling.key = father.key
+ else
+ father.consumed++ //At least one shadowling
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Вы хотите поиграть за тенелинга?", ROLE_SHADOWLING, TRUE, 25 SECONDS, source = /mob/living/simple_animal/demon/shadow_father, role_cleanname = "Shadowling")
+ while(father.consumed > 0)
+ if(candidates.len < 1)
+ break
+ var/player_to_spawn
+ var/mob/dead/observer/ghost = pick(candidates)
+ candidates.Remove(ghost)
+ player_to_spawn = ghost.key
+ var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(father.loc)
+ var/mob/living/carbon/human/shadowling/ling = new /mob/living/carbon/human/shadowling(holder)
+ ling.AddSpell(new /datum/spell/shadowling/self/rift_in)
+ dust_if_respawnable(ghost)
+ ling.key = player_to_spawn
+ father.consumed--
+
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_language.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_language.dm
new file mode 100644
index 000000000000..25ea1cbf3fd9
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_language.dm
@@ -0,0 +1,18 @@
+/datum/language/shadowling
+ name = "Шёпот из тени"
+ desc = "В тени всегда есть те, кто слышит все ваши секреты. Шадоулинги используют рэдспейс для связи между собой."
+ speech_verb = "says"
+ colour = "purple"
+ key = "sl"
+ flags = RESTRICTED | HIVEMIND | NOBABEL
+ follow = TRUE
+
+/datum/language/shadowling/get_random_name()
+ var/new_name
+ if(prob(0.1))
+ new_name = "Лучик Доброты" // :)
+ else
+ // Edgelord names ahead
+ new_name += "[pick("Владыка", "Поработитель", "Повелитель", "Мучитель", "Пожиратель", "Угнетатель", "Судья", "Убийца", "Уничтожитель", "Тиран")]"
+ new_name += " [pick("Тьмы", "Тени", "Темноты", "Ужаса", "Страха", "Ненависти", "Зависти", "Злости", "Греха", "Мрака", "Боли", "Кары")]"
+ return new_name
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_mob.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_mob.dm
new file mode 100644
index 000000000000..087b254f914b
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_mob.dm
@@ -0,0 +1,3 @@
+// Not subtype of /mob/living/carbon/human/shadow because of weird parent proc call
+/mob/living/carbon/human/shadowling/Initialize(mapload)
+ . = ..(mapload, /datum/species/shadow/ling)
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_species.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_species.dm
new file mode 100644
index 000000000000..1c5c511e0283
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_species.dm
@@ -0,0 +1,74 @@
+#define LIGHT_DAM_THRESHOLD 4
+#define LIGHT_HEAL_THRESHOLD 2
+#define LIGHT_DAMAGE_TAKEN 6
+
+/datum/species/shadow/ling
+ //Normal shadowpeople but with enhanced effects
+ name = "Shadowling"
+ name_plural = "Shadowlings"
+
+ default_language = "Шёпот из тени"
+ language = "Шёпот из тени"
+ icobase = 'modular_ss220/antagonists/icons/shadowlings/r_shadowling.dmi'
+ blacklisted = TRUE
+
+ blood_color = "#555555"
+ flesh_color = "#222222"
+
+ species_traits = list(NO_BLOOD, NO_HAIR, NOT_SELECTABLE)
+ inherent_traits = list(TRAIT_RESISTHEAT, TRAIT_NOBREATH, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE, TRAIT_CHUNKYFINGERS, TRAIT_NOPAIN, TRAIT_NO_BONES, TRAIT_STURDY_LIMBS, TRAIT_XENO_IMMUNE, TRAIT_NOHUNGER)
+
+ burn_mod = 1.25
+ heatmod = 1.5
+
+/datum/species/shadow/ling/proc/handle_light(mob/living/carbon/human/H)
+ var/light_amount = 0
+ if(isturf(H.loc))
+ var/turf/T = H.loc
+ light_amount = T.get_lumcount() * 10
+ if(light_amount > LIGHT_DAM_THRESHOLD && !H.incorporeal_move) //Can survive in very small light levels. Also doesn't take damage while incorporeal, for shadow walk purposes
+ H.throw_alert("lightexposure", /atom/movable/screen/alert/lightexposure)
+ if(is_species(H, /datum/species/shadow/ling/lesser))
+ H.take_overall_damage(0, LIGHT_DAMAGE_TAKEN/2)
+ else
+ H.take_overall_damage(0, LIGHT_DAMAGE_TAKEN)
+ if(H.stat != DEAD)
+ to_chat(H, span_userdanger("Свет жжёт вас!"))
+ H << 'sound/weapons/sear.ogg'
+ else if(light_amount < LIGHT_HEAL_THRESHOLD)
+ H.clear_alert("lightexposure")
+ var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes)
+ if(istype(E))
+ E.receive_damage(-1)
+ if(is_species(H, /datum/species/shadow/ling/lesser))
+ H.heal_overall_damage(2, 3)
+ else
+ H.heal_overall_damage(5, 7)
+ H.adjustToxLoss(-5)
+ H.adjustBrainLoss(-25)
+ H.AdjustEyeBlurry(-2 SECONDS)
+ H.adjustCloneLoss(-1)
+ H.SetWeakened(0)
+ H.SetStunned(0)
+
+ else
+ if(H.health <= HEALTH_THRESHOLD_CRIT) // to finish shadowlings in rare occations
+ H.adjustBruteLoss(1)
+
+/datum/species/shadow/ling/handle_life(mob/living/carbon/human/H)
+ ..(H)
+ handle_light(H)
+
+/datum/species/shadow/ling/lesser //Empowered thralls. Obvious, but powerful
+ name = "Lesser Shadowling"
+
+ icobase = 'modular_ss220/antagonists/icons/shadowlings/r_lshadowling.dmi'
+ blood_color = "#CCCCCC"
+ flesh_color = "#AAAAAA"
+
+ burn_mod = 1.1
+ heatmod = 1.2
+
+#undef LIGHT_DAM_THRESHOLD
+#undef LIGHT_HEAL_THRESHOLD
+#undef LIGHT_DAMAGE_TAKEN
diff --git a/modular_ss220/antagonists/code/shadowlings/shadowling_traps.dm b/modular_ss220/antagonists/code/shadowlings/shadowling_traps.dm
new file mode 100644
index 000000000000..6bf24607288b
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/shadowling_traps.dm
@@ -0,0 +1,183 @@
+#define MAX_SHADOWLING_TRAPS 6
+
+/obj/structure/shadow_trap
+ name = "тёмное пятно"
+ desc = "Большое тёмное пятно на полу, стенах и потолке. Вы не уверены что это такое, но лучше держаться подальше."
+ var/trap_adjective = "сломанная"
+ var/hud_icon_state = "broken"
+ icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_traps.dmi'
+ icon_state = "trap"
+ anchored = TRUE
+ opacity = FALSE
+ layer = ABOVE_OBJ_LAYER
+ max_integrity = 50
+ hud_possible = list(SPECIALROLE_HUD)
+
+ var/created_by
+
+/obj/structure/shadow_trap/Initialize(mapload)
+ . = ..()
+ prepare_huds()
+
+/// This one is used to change path to special role hud icon file for modular behavior
+/obj/structure/shadow_trap/prepare_huds()
+ ..()
+ // Changing path to special_role HUD
+ var/image/I = image('modular_ss220/antagonists/icons/shadowlings/shadowlings_traps.dmi', src, "")
+ I.appearance_flags = RESET_COLOR | RESET_TRANSFORM
+ hud_list[SPECIALROLE_HUD] = I
+ // Toggle visibility
+ var/datum/atom_hud/hud = GLOB.huds[ANTAG_HUD_SHADOW]
+ hud.add_to_hud(src)
+ // Changing icon_state of holder
+ I.icon_state = hud_icon_state
+
+
+/obj/structure/shadow_trap/Crossed(atom/movable/AM, oldloc)
+ . = ..()
+ if(is_shadow_or_thrall(AM))
+ return
+ if(!trap_activate(AM)) // Returns false if trap isn't triggered by this type
+ return
+ if(created_by)
+ to_chat(created_by, span_purple("Ваша [trap_adjective] ловушка сработала на [AM] в [get_turf(AM.loc)]"))
+ else
+ created_by = "Admin spawn"
+ add_attack_logs(AM, AM, "activated shadowling trap placed by [created_by]", ATKLOG_ALL)
+ Destroy()
+
+/obj/structure/shadow_trap/proc/trap_activate(mob/living/target)
+ return
+
+/obj/structure/shadow_trap/examine(mob/user)
+ . = ..()
+ if(is_shadow_or_thrall(user))
+ . += "Это [trap_adjective] ловушка."
+
+/obj/structure/shadow_trap/Destroy()
+ if(!istype(created_by, /mob/living/simple_animal/demon/shadow_father))
+ return ..()
+ var/mob/living/simple_animal/demon/shadow_father/father = created_by
+ father.placed_traps.Remove(src)
+ . = ..()
+
+// Spell
+
+/datum/spell/shadowling/self/place_trap
+ name = "Установить ловушку"
+ action_icon_state = "vampire_glare"
+ base_cooldown = 30 SECONDS
+ stat_allowed = UNCONSCIOUS
+ var/trap_type = /obj/structure/shadow_trap
+
+/datum/spell/shadowling/self/place_trap/can_cast(mob/user, charge_check, show_message)
+ . = ..()
+ if(!istype(user, /mob/living/simple_animal/demon/shadow_father))
+ to_chat(user, span_warning("Вы должны быть отцом тьмы для установки ловушек."))
+ return FALSE
+ var/mob/living/simple_animal/demon/shadow_father/father = user
+ if(ismob(father.pulling))
+ to_chat(user, span_warning("Вы не можете устанавливать ловушки, пока тащите кого-то."))
+ return FALSE
+ if(father.placed_traps.len >= MAX_SHADOWLING_TRAPS)
+ to_chat(user, span_warning("Вы построили уже построили максимум ([MAX_SHADOWLING_TRAPS]) ловушек. Разрушьте старые для установки новых."))
+ return FALSE
+ return
+
+/datum/spell/shadowling/self/place_trap/cast(list/targets, mob/user)
+ var/mob/living/simple_animal/demon/shadow_father/father = user
+ var/obj/structure/shadow_trap/trap = new trap_type(get_turf(father))
+ father.placed_traps.Add(trap)
+ trap.created_by = father
+
+ for(var/datum/spell/shadowling/self/place_trap/spell in user.mob_spell_list)
+ if(spell != src)
+ spell.cooldown_handler.start_recharge()
+
+// Stun Trap
+// Disable headset, stun for 6 seconds and disable light
+
+/obj/structure/shadow_trap/stun
+ trap_adjective = "оглущающая"
+ hud_icon_state = "stun"
+
+/obj/structure/shadow_trap/stun/trap_activate(mob/living/target)
+ if(ishuman(target))
+ var/mob/living/carbon/human/human_target = target
+ to_chat(human_target, span_boldwarning("Вас что-то схватило за ногу!"))
+ human_target.emote("scream")
+ human_target.KnockDown(6 SECONDS)
+ human_target.Confused(7 SECONDS)
+ human_target.extinguish_light()
+ for(var/obj/item/headset in human_target.contents)
+ if(istype(headset, /obj/item/radio))
+ headset.emp_act()
+ return TRUE
+ if(isrobot(target))
+ var/mob/living/silicon/robot/robot_target = target
+ to_chat(robot_target, span_boldwarning("ОШИБКА: Неизвестное проникновение в п$о%#@*&..."))
+ robot_target << 'sound/misc/interference.ogg'
+ playsound(robot_target, 'sound/machines/warning-buzzer.ogg', 50, TRUE)
+ do_sparks(5, 1, robot_target)
+ robot_target.extinguish_light()
+ robot_target.Weaken(6 SECONDS)
+ return TRUE
+ return FALSE
+
+/datum/spell/shadowling/self/place_trap/stun
+ name = "Установить оглущающую ловушку"
+ trap_type = /obj/structure/shadow_trap/stun
+ action_icon_state = "stun_trap"
+
+// Poison Trap
+// Deal burn + toxin damage to target and force them to sleep
+
+/obj/structure/shadow_trap/poison
+ trap_adjective = "отравляющая"
+ hud_icon_state = "poison"
+
+/obj/structure/shadow_trap/poison/trap_activate(mob/living/target)
+ if(!ishuman(target))
+ return FALSE
+ if(ismachineperson(target))
+ return FALSE
+ if(target.reagents)
+ to_chat(target, span_boldwarning("Вас что-то ужалило за ногу!"))
+ target.reagents.add_reagent("frostoil", 30)
+ target.reagents.add_reagent("neurotoxin", 30)
+ return TRUE
+ return FALSE
+
+/datum/spell/shadowling/self/place_trap/poison
+ name = "Установить ядовитую ловушку"
+ trap_type = /obj/structure/shadow_trap/poison
+ action_icon_state = "poison_trap"
+
+// Blindness Trap
+// Deal damage to eyes and create blindsmoke cloud
+
+/obj/structure/shadow_trap/blindness
+ trap_adjective = "ослепляющая"
+ hud_icon_state = "blindness"
+
+/obj/structure/shadow_trap/blindness/trap_activate(mob/living/target)
+ if(!ishuman(target))
+ return FALSE
+ to_chat(target, span_boldwarning("С потолка на вас капает чёрная вязкая слизь. Она начинает выделять огромное колличество чёрного дыма!"))
+ playsound(src, 'sound/effects/bamf.ogg', 50, TRUE)
+ var/datum/reagents/reagents_list = new (1000)
+ reagents_list.add_reagent("blindness_smoke", 810)
+ var/datum/effect_system/smoke_spread/chem/chem_smoke = new
+ chem_smoke.set_up(reagents_list, loc, TRUE)
+ chem_smoke.start(4)
+ if(target.reagents)
+ target.reagents.add_reagent("blindness_smoke", 30)
+ var/obj/item/organ/internal/eyes/eyes = target.get_int_organ(/obj/item/organ/internal/eyes)
+ eyes.receive_damage(15, 1)
+ return TRUE
+
+/datum/spell/shadowling/self/place_trap/blindness
+ name = "Установить ослепляющую ловушку"
+ trap_type = /obj/structure/shadow_trap/blindness
+ action_icon_state = "blindness_trap"
+
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadow_father_crawl.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadow_father_crawl.dm
new file mode 100644
index 000000000000..6ce0bab64366
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadow_father_crawl.dm
@@ -0,0 +1,4 @@
+// Can be used even while on light
+
+/datum/spell/bloodcrawl/shadow_crawl/father/valid_target(turf/target, user)
+ return TRUE
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_blindness_smoke.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_blindness_smoke.dm
new file mode 100644
index 000000000000..4d70233b3fed
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_blindness_smoke.dm
@@ -0,0 +1,39 @@
+/datum/spell/shadowling/self/blindness_smoke
+ name = "Ослепляющий дым"
+ desc = "Вы выдыхаете облако густого ослепляющего врагов дыма, однако лечащего лояльных тьме слуг."
+ base_cooldown = 30 SECONDS
+ stat_allowed = UNCONSCIOUS
+ action_icon_state = "blindness_smoke"
+
+
+/datum/spell/shadowling/self/blindness_smoke/cast(list/targets, mob/user)
+ user.visible_message(span_warning("[user] внезапно выдыхает облако чёрного дыма, который начинает быстро распространяться!"))
+ playsound(user, 'sound/effects/bamf.ogg', 50, TRUE)
+ var/datum/reagents/reagents_list = new (1000)
+ reagents_list.add_reagent("blindness_smoke", 810)
+ var/datum/effect_system/smoke_spread/chem/chem_smoke = new
+ chem_smoke.set_up(reagents_list, user.loc, TRUE)
+ chem_smoke.start(4)
+
+/datum/reagent/shadowling_blindness_smoke //Blinds non-shadowlings, heals shadowlings/thralls
+ name = "odd black liquid"
+ id = "blindness_smoke"
+ description = "<::ОШИБКА::> НЕВОЗМОЖНО ПРОАНАЛИЗИРОВАТЬ РЕАГЕНТ <::ОШИБКА::>"
+ color = "#000000" //Complete black (RGB: 0, 0, 0)
+ metabolization_rate = 250 * REAGENTS_METABOLISM //still lel
+
+
+/datum/reagent/shadowling_blindness_smoke/on_mob_life(mob/living/M)
+ var/update_flags = STATUS_UPDATE_NONE
+ if(!is_shadow_or_thrall(M))
+ to_chat(M, span_boldwarning("Вы вдыхаете чёрный дым и ваши глаза начинают ужасно болеть!"))
+ M.EyeBlind(10 SECONDS)
+ if(prob(25))
+ M.visible_message(span_warning("[M] трёт свои глаза."))
+ M.Stun(4 SECONDS)
+ else
+ to_chat(M, span_notice("Вы вдыхаете чёрный дым и чувствуете облегчение!"))
+ update_flags |= M.heal_organ_damage(10, 10, updating_health = FALSE)
+ update_flags |= M.adjustOxyLoss(-10, FALSE)
+ update_flags |= M.adjustToxLoss(-10, FALSE)
+ return ..() | update_flags
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_enthrall.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_enthrall.dm
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_glare.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_glare.dm
new file mode 100644
index 000000000000..a9ef5fc4332d
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_glare.dm
@@ -0,0 +1,106 @@
+// Vampire glare with some additions
+
+/// No deviation at all. Flashed from the front or front-left/front-right. Alternatively, flashed in direct view.
+#define DEVIATION_NONE 3
+/// Partial deviation. Flashed from the side. Alternatively, flashed out the corner of your eyes.
+#define DEVIATION_PARTIAL 2
+/// Full deviation. Flashed from directly behind or behind-left/behind-rack. Not flashed at all.
+#define DEVIATION_FULL 1
+
+/// Light level, required to apply additional effects
+#define GLARE_REQUIRED_DARKNESS 4
+
+/datum/spell/shadowling/glare
+ name = "Блик"
+ desc = "Ваши красные глаза сверкают, завораживая и очаровывая смертных перед вами. Для лучшего эффекта необходим близкий зрительный контакт и тёмное окружение."
+ base_cooldown = 40 SECONDS
+ stat_allowed = UNCONSCIOUS
+ action_icon_state = "glare"
+
+/datum/spell/shadowling/glare/can_cast(mob/user, charge_check, show_message)
+ . = ..()
+ // Veil shouldn't work in shadow crawl
+ if(istype(user.loc, /obj/effect/dummy/slaughter))
+ return FALSE
+
+/datum/spell/shadowling/glare/create_new_targeting()
+ var/datum/spell_targeting/aoe/T = new
+ T.allowed_type = /mob/living
+ T.range = 1
+ return T
+
+/datum/spell/shadowling/glare/create_new_cooldown()
+ var/datum/spell_cooldown/charges/C = new
+ C.max_charges = 2
+ C.recharge_duration = base_cooldown
+ C.charge_duration = 2 SECONDS
+ return C
+
+/datum/spell/shadowling/glare/proc/calculate_deviation(mob/victim, mob/attacker)
+
+ // If the victim was looking at the attacker, this is the direction they'd have to be facing.
+ var/attacker_to_victim = get_dir(attacker, victim)
+ // The victim's dir is necessarily a cardinal value.
+ var/attacker_dir = attacker.dir
+
+ // - - -
+ // - V - Attacker facing south
+ // # # #
+ // Attacker within 45 degrees of where the victim is facing.
+ if(attacker_dir & attacker_to_victim)
+ return DEVIATION_NONE
+ // Are they on the same tile? This is probably the victim crawling under the vampire, and looking down shouldn't be too tough.
+ if(victim.loc == attacker.loc)
+ return DEVIATION_NONE
+ // # # #
+ // - V - Attacker facing south
+ // - - -
+ // Victim at 135 or more degrees of where the victim is facing.
+ if(attacker_dir & REVERSE_DIR(attacker_to_victim))
+ return DEVIATION_FULL
+ // - - -
+ // # V # Attacker facing south
+ // - - -
+ // Victim lateral to the victim.
+ return DEVIATION_PARTIAL
+
+/datum/spell/shadowling/glare/cast(list/targets, mob/living/user = usr)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(istype(H.glasses, /obj/item/clothing/glasses/sunglasses/blindfold))
+ var/obj/item/clothing/glasses/sunglasses/blindfold/B = H.glasses
+ if(B.tint)
+ to_chat(user, span_warning("Ваши глаза закрыты! Взгляд не работает!"))
+ return
+ user.mob_light(LIGHT_COLOR_BLOOD_MAGIC, 3, _duration = 2)
+ user.visible_message(span_warning("Вы используйете очарование своих глаз, вводя в замешательство окружающих"))
+
+ for(var/mob/living/target in targets)
+ if(shadowling_check(target))
+ continue
+
+ var/deviation
+ if(IS_HORIZONTAL(user))
+ deviation = DEVIATION_PARTIAL
+ else
+ deviation = calculate_deviation(target, user)
+ if(deviation == DEVIATION_FULL)
+ target.Confused(6 SECONDS)
+ target.apply_damage(20, STAMINA)
+ else if((deviation == DEVIATION_PARTIAL) || (get_light_level(target) > GLARE_REQUIRED_DARKNESS))
+ target.KnockDown(5 SECONDS)
+ target.Confused(6 SECONDS)
+ target.apply_damage(40, STAMINA)
+ else
+ target.Confused(10 SECONDS)
+ target.apply_damage(70, STAMINA)
+ target.KnockDown(12 SECONDS)
+ target.AdjustSilence(8 SECONDS)
+ target.flash_eyes(1, TRUE, TRUE)
+ to_chat(target, span_warning("Глаза [user] источают чарующую красную ауру. Ваше тело слабеет..."))
+ add_attack_logs(user, target, "(Shadowling) Glared at")
+
+#undef DEVIATION_NONE
+#undef DEVIATION_PARTIAL
+#undef DEVIATION_FULL
+#undef GLARE_REQUIRED_DARKNESS
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_hatch.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_hatch.dm
new file mode 100644
index 000000000000..bc9d979c9a31
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_hatch.dm
@@ -0,0 +1,125 @@
+/obj/structure/alien/resin/wall/shadowling
+ name = "Теневая стена"
+ desc = "Стена из дымящейся чёрной материи, напоминающей смолу. Выглядит неразрушимой."
+ max_integrity = INFINITY
+
+/datum/spell/shadowling/self/hatch
+ name = "Вылупиться"
+ desc = "Вы окружаете себя теневым коконом и разрываете оболочку, обнажая свою истинную форму. Лучше использовать в укромном месте"
+ stat_allowed = CONSCIOUS
+ base_cooldown = 5 MINUTES
+ action_icon_state = "ascend"
+
+/datum/spell/shadowling/self/hatch/can_cast(mob/user, charge_check, show_message)
+ if(!ishuman(user))
+ return FALSE
+ if(!isturf(user.loc))
+ return FALSE
+ if(istype(user.loc, /turf/space))
+ return FALSE
+ . = ..()
+
+/datum/spell/shadowling/self/hatch/cast(list/targets, mob/user)
+ . = ..()
+ var/mob/living/carbon/human/shadowling = user
+ if(!istype(shadowling))
+ return FALSE
+ var/result = tgui_alert(shadowling, "Вы уверены что хотите скинуть оболочку? Это займёт некоторое время и после этого всем станет ясна ваша сущность.", "Вылупиться?", list("Да", "Нет"))
+ if(result != "Да")
+ return FALSE
+
+ for(var/obj/item/item in shadowling.get_equipped_items(include_pockets = TRUE))
+ shadowling.unEquip(item, TRUE)
+
+ sleep(5 SECONDS)
+
+ // Creating cocoon
+ var/turf/simulated/floor/F
+ var/turf/shadowturf = get_turf(shadowling)
+ for(F in orange(1, shadowling))
+ new /obj/structure/alien/resin/wall/shadowling(F)
+
+ for(var/obj/structure/alien/resin/wall/shadowling/R in shadowturf)
+ qdel(R)
+
+ // Temporal godmode and stun for shadowling
+
+ shadowling.dna.species.brute_mod = 0;
+ shadowling.dna.species.burn_mod = 0;
+ shadowling.dna.species.tox_mod = 0;
+ shadowling.dna.species.oxy_mod = 0;
+ shadowling.dna.species.clone_mod = 0;
+ shadowling.dna.species.brain_mod = 0;
+ shadowling.Stun(INFINITY) // Until hatching complete
+
+ shadowling.visible_message(
+ span_warning("Тёмная мембрана образуется вокруг [shadowling]. Изнутри начинают доноситься странные булькающие звуки.."),
+ span_notice("Вы начинаете свою метаморфозу. Внутри этого кокона вы вскоре избавитесь от ограничевающей вас оболочки.")
+ )
+ sleep(10 SECONDS)
+
+ shadowling.visible_message(
+ span_warning("Кожа [shadowling] пузырится и неествественно извивается, постепенно отваливаясь целыми лоскутами. За ней виднеется чёрная плоть."),
+ span_notice("Кожа начинает постепенно сползать с вас. Вы чувствуете как ваша истинная сила возвращается к вам.")
+ )
+ sleep(10 SECONDS)
+
+ sleep(8 SECONDS)
+ playsound(shadowling.loc, 'sound/weapons/slash.ogg', 25, TRUE)
+ to_chat(shadowling, span_purple("Вы начинаете вырываться из кокона."))
+
+ sleep(1 SECONDS)
+ playsound(shadowling.loc, 'sound/weapons/slashmiss.ogg', 25, TRUE)
+ to_chat(shadowling, span_purple("Кокон рушится под вашими ударами."))
+
+ sleep(1 SECONDS)
+ playsound(shadowling.loc, 'sound/weapons/slice.ogg', 25, TRUE)
+ to_chat(shadowling, span_purple("ВЫ СВОБОДНЫ!"))
+
+ sleep(1 SECONDS)
+ playsound(shadowling.loc, 'sound/effects/ghost.ogg', 50, TRUE)
+
+ // Removing cocoon
+ for(var/obj/structure/alien/resin/wall/shadowling/resin in orange(shadowling, 1))
+ playsound(resin, 'sound/effects/splat.ogg', 50, TRUE)
+ qdel(resin)
+
+ for(var/obj/structure/alien/weeds/node/node in shadowturf)
+ qdel(node)
+
+ // Transformation
+ shadowling.set_species(/datum/species/shadow/ling)
+
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/under/shadowling(user), SLOT_HUD_JUMPSUIT)
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/shoes/shadowling(user), SLOT_HUD_SHOES)
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(user), SLOT_HUD_OUTER_SUIT)
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(user), SLOT_HUD_HEAD)
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/gloves/shadowling(user), SLOT_HUD_GLOVES)
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/shadowling(user), SLOT_HUD_WEAR_MASK)
+ shadowling.equip_to_slot_or_del(new /obj/item/clothing/glasses/shadowling(user), SLOT_HUD_GLASSES)
+
+ shadowling.name = random_name(MALE, "Shadowling")
+ shadowling.real_name = shadowling.name
+ shadowling.SetStunned(0)
+ shadowling.underwear = "Nude"
+ shadowling.undershirt = "Nude"
+ shadowling.socks = "Nude"
+
+ shadowling.ExtinguishMob()
+ shadowling.set_nutrition(NUTRITION_LEVEL_FED + 50)
+
+ // Granting spells
+ var/list/datum/spell/spells_to_grant = list(
+ new /datum/spell/shadowling/glare,
+ new /datum/spell/shadowling/veil,
+ new /datum/spell/shadowling/screech,
+ new /datum/spell/shadowling/icy_veins,
+ // TODO
+ // new /datum/spell/shadowling/entrall,
+ new /datum/spell/shadowling/self/blindness_smoke,
+ new /datum/spell/shadowling/self/shadow_walk
+ )
+ for(var/datum/spell/spell in spells_to_grant)
+ shadowling.AddSpell(spell)
+
+ shadowling.RemoveSpell(src)
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_icy_veins.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_icy_veins.dm
new file mode 100644
index 000000000000..d003a2bf1043
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_icy_veins.dm
@@ -0,0 +1,28 @@
+/datum/spell/shadowling/icy_veins
+ name = "Стынущие жилы"
+ desc = "Вы замораживаете кровь в венах окружающих вас врагов в радиусе 4-х клеток, замедляя их и нанося им урон от холода."
+ base_cooldown = 35 SECONDS
+ stat_allowed = UNCONSCIOUS
+ action_icon_state = "icy_veins"
+
+/datum/spell/shadowling/icy_veins/create_new_targeting()
+ var/datum/spell_targeting/aoe/T = new
+ T.allowed_type = /mob/living
+ T.range = 4
+ return T
+
+/datum/spell/shadowling/icy_veins/cast(list/targets, mob/living/user = usr)
+ to_chat(user, span_purple("Вы посылаете волну холода."))
+ playsound(user.loc, 'sound/effects/ghost2.ogg', 50, TRUE)
+
+ for(var/mob/living/carbon/target in targets)
+ if(shadowling_check(target))
+ to_chat(target, span_warning("Вы чувствуете волну холода, но для вас она безвредна"))
+ continue
+ to_chat(target, span_danger("Вас пронзает волна холода! У вас буквально стынет кровь в жилах!"))
+ target.Stun(2 SECONDS)
+ target.apply_damage(10, BURN)
+ target.adjust_bodytemperature(-200) //Extreme amount of initial cold
+ if(target.reagents)
+ target.reagents.add_reagent("frostoil", 15) //Half of a cryosting
+
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_rift_in.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_rift_in.dm
new file mode 100644
index 000000000000..6e4fddd1fb87
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_rift_in.dm
@@ -0,0 +1,14 @@
+/datum/spell/shadowling/self/rift_in
+ name = "Проникнуть через разлом"
+ desc = "Вы проникаете через разрыв из мира теней в материальную реальность"
+ stat_allowed = UNCONSCIOUS
+ action_icon_state = "ascend"
+
+/datum/spell/shadowling/self/rift_in/cast(list/targets, mob/user = usr)
+ var/obj/effect/dummy/slaughter/holder = user.loc
+ if(istype(holder))
+ user.forceMove(holder.loc)
+ qdel(holder)
+ user.RemoveSpell(src)
+ return
+
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_screech.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_screech.dm
new file mode 100644
index 000000000000..25eda6727db1
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_screech.dm
@@ -0,0 +1,40 @@
+/datum/spell/shadowling/screech
+ name = "Вопль"
+ desc = "Вы громко вопите, дизориентируя врагов, отключая боргов и повреждая стёкла в радиусе 5-и клеток."
+ base_cooldown = 40 SECONDS
+ stat_allowed = UNCONSCIOUS
+ create_attack_logs = FALSE // Required to stop turf spam to logs
+ action_icon_state = "screech"
+
+/datum/spell/shadowling/screech/create_new_targeting()
+ var/datum/spell_targeting/aoe/turf/T = new
+ T.range = 5
+ return T
+
+/datum/spell/shadowling/screech/cast(list/targets, mob/user)
+ user.audible_message(span_boldwarning("[user] издаёт ужасающий крик!"))
+ playsound(user.loc, 'sound/effects/screech.ogg', 100, TRUE)
+
+ for(var/turf/turf in targets)
+ for(var/mob/target in turf.contents)
+ if(shadowling_check(target))
+ continue
+
+ if(iscarbon(target))
+ var/mob/living/carbon/c_mob = target
+ to_chat(c_mob, span_boldwarning("Боль пронзает вашу голову! Этот звук НЕВЫНОСИМ!"))
+ c_mob.AdjustConfused(20 SECONDS)
+ c_mob.AdjustDeaf(6 SECONDS)
+
+ else if(issilicon(target))
+ var/mob/living/silicon/robot = target
+ to_chat(robot, span_boldwarning("ОШИБКА: Внешние сенсоры пе&рег?3%8@_#..."))
+ robot << 'sound/misc/interference.ogg'
+ playsound(robot, 'sound/machines/warning-buzzer.ogg', 50, TRUE)
+ do_sparks(5, 1, robot)
+ robot.Weaken(12 SECONDS)
+
+ add_attack_logs(user, target, "cast the spell [name]", ATKLOG_ALL)
+
+ for(var/obj/structure/window/window in turf.contents)
+ window.take_damage(rand(80, 100))
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_shadow_walk.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_shadow_walk.dm
new file mode 100644
index 000000000000..f32dbad5fb84
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_shadow_walk.dm
@@ -0,0 +1,28 @@
+/datum/spell/shadowling/self/shadow_walk
+ name = "Шаг в тень"
+ desc = "Вы просачиваетесь в пространство меж миров, получая способность быстро перемещаться сквозь стены на 4 секунды. Свет по-прежнему опасен для вас."
+ base_cooldown = 60 SECONDS
+ stat_allowed = UNCONSCIOUS
+ action_icon_state = "shadowling_crawl"
+
+/datum/spell/shadowling/self/shadow_walk/cast(list/targets, mob/living/user = usr)
+ playsound(user.loc, 'sound/effects/bamf.ogg', 50, 1)
+ user.visible_message(span_warning("[user] исчезает в облаке тёмного тумана!"), span_purple("Вы просачиваетесь в пространство меж миров на 4 секунды."))
+ user.SetStunned(0)
+ user.SetWeakened(0)
+ user.SetKnockDown(0)
+ user.incorporeal_move = INCORPOREAL_MOVE_NORMAL
+ user.alpha = 0
+ user.ExtinguishMob()
+ user.forceMove(get_turf(user)) //to properly move the mob out of a potential container
+ user.pulledby?.stop_pulling()
+ user.stop_pulling()
+
+ sleep(4 SECONDS)
+ if(QDELETED(user))
+ return
+
+ user.visible_message(span_warning("[user] внезапно появляется из ниоткуда!"), span_purple("Давление реальности становится невыносимым. Вы вынуждены вернуться в материальный мир"))
+ user.incorporeal_move = NO_INCORPOREAL_MOVE
+ user.alpha = 255
+ user.forceMove(get_turf(user))
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_spell.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_spell.dm
new file mode 100644
index 000000000000..7ab08b77c9c4
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_spell.dm
@@ -0,0 +1,42 @@
+// Basic
+
+/datum/spell/shadowling
+ school = "shadowling"
+ action_background_icon_state = "shadow_demon_bg"
+ action_icon = 'modular_ss220/antagonists/icons/shadowlings/shadowlings_actions.dmi'
+ clothes_req = FALSE
+
+ /// If true, can be casted while using shadowstep and other incorporeal abilities
+ var/cast_incorporeal = FALSE
+
+/datum/spell/shadowling/can_cast(mob/user, charge_check, show_message)
+ if(isliving(user))
+ var/mob/living/living_user = user
+ if(living_user.incorporeal_move != NO_INCORPOREAL_MOVE)
+ to_chat(user, span_warning("Вы не можете использовать эту способность не в физической форме!"))
+ return
+ return ..()
+
+/datum/spell/shadowling/proc/get_light_level(mob/living/target)
+ var/light_amount = 0
+ if(isturf(target.loc))
+ var/turf/T = target.loc
+ light_amount = T.get_lumcount() * 10
+ return light_amount
+
+/datum/spell/shadowling/proc/shadowling_check(mob/living/carbon/human/user)
+ if(!istype(user))
+ return FALSE
+
+ if(isshadowling(user) && is_shadow(user))
+ return TRUE
+
+ if(isshadowlinglesser(user) && is_thrall(user))
+ return TRUE
+
+ return FALSE
+
+// Self
+
+/datum/spell/shadowling/self/create_new_targeting()
+ return new /datum/spell_targeting/self
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_tear_the_reality.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_tear_the_reality.dm
new file mode 100644
index 000000000000..3c8b5067f473
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_tear_the_reality.dm
@@ -0,0 +1,24 @@
+/datum/spell/shadowling/self/tear_the_reality
+ name = "Прорвать реальность"
+ desc = "Вы используете накопленную от поглощённых смертных силу, чтобы создать разрыв в реальности и призвать в этот мир своих детей. Летально для вас, но гарантирует то, что ваш дух возродится в одном из детей."
+ base_cooldown = 0 SECONDS
+ stat_allowed = UNCONSCIOUS
+ action_icon_state = "ascend"
+
+/datum/spell/shadowling/self/tear_the_reality/can_cast(mob/user, charge_check, show_message)
+ . = ..()
+ if(!istype(user, /mob/living/simple_animal/demon/shadow_father))
+ return FALSE
+ var/mob/living/simple_animal/demon/shadow_father/father = user
+ if(father.consumed < 1)
+ to_chat(father, span_warning("Вам нужно поглотить как минимум одного смертного, чтобы прорвать реальность."))
+ return FALSE
+
+/datum/spell/shadowling/self/tear_the_reality/cast(list/targets, mob/user)
+ var/mob/living/simple_animal/demon/shadow_father/father = user
+ var/confirm = tgui_alert(father, "Вы уверены что хотите закончить свою охоту и прорвать реальность? Вы призовёте [father.consumed + 1] тенелингов и гарантированно станите одним из них.", "Закончить охоту?", list("Да", "Я ещё поохочусь"))
+ if(confirm != "Да")
+ return
+ INVOKE_ASYNC(SSticker.mode, TYPE_PROC_REF(/datum/game_mode, begin_shadowling_invasion), src, TRUE)
+ qdel(user)
+
diff --git a/modular_ss220/antagonists/code/shadowlings/spells/shadowling_veil.dm b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_veil.dm
new file mode 100644
index 000000000000..37587d25a5bc
--- /dev/null
+++ b/modular_ss220/antagonists/code/shadowlings/spells/shadowling_veil.dm
@@ -0,0 +1,26 @@
+/datum/spell/shadowling/veil
+ name = "Пелена"
+ desc = "Вы отключаете большинство источников освещения в радиусе 5-и клеток"
+ base_cooldown = 15 SECONDS
+ stat_allowed = UNCONSCIOUS
+ create_attack_logs = FALSE // Required to stop turf spam to logs
+ action_icon_state = "veil"
+
+/datum/spell/shadowling/veil/create_new_targeting()
+ var/datum/spell_targeting/aoe/turf/T = new
+ T.range = 5
+ return T
+
+/datum/spell/shadowling/veil/can_cast(mob/user, charge_check, show_message)
+ . = ..()
+ // Veil shouldn't work in shadow crawl
+ if(istype(user.loc, /obj/effect/dummy/slaughter))
+ return FALSE
+
+/datum/spell/shadowling/veil/cast(list/targets, mob/user = usr)
+ add_attack_logs(user, user, "cast the spell [name]", ATKLOG_ALL)
+ to_chat(user, span_purple("Вы бесшумно отключаете источники света поблизости."))
+ for(var/turf/T in targets)
+ T.extinguish_light()
+ for(var/atom/A in T.contents)
+ A.extinguish_light()
diff --git a/modular_ss220/antagonists/icons/shadowlings/father_of_shadows.dmi b/modular_ss220/antagonists/icons/shadowlings/father_of_shadows.dmi
new file mode 100644
index 000000000000..297558f676e0
Binary files /dev/null and b/modular_ss220/antagonists/icons/shadowlings/father_of_shadows.dmi differ
diff --git a/modular_ss220/antagonists/icons/shadowlings/r_lshadowling.dmi b/modular_ss220/antagonists/icons/shadowlings/r_lshadowling.dmi
new file mode 100644
index 000000000000..0e955e5bd977
Binary files /dev/null and b/modular_ss220/antagonists/icons/shadowlings/r_lshadowling.dmi differ
diff --git a/modular_ss220/antagonists/icons/shadowlings/r_shadowling.dmi b/modular_ss220/antagonists/icons/shadowlings/r_shadowling.dmi
new file mode 100644
index 000000000000..e630750aeb6c
Binary files /dev/null and b/modular_ss220/antagonists/icons/shadowlings/r_shadowling.dmi differ
diff --git a/modular_ss220/antagonists/icons/shadowlings/shadowlings_actions.dmi b/modular_ss220/antagonists/icons/shadowlings/shadowlings_actions.dmi
new file mode 100644
index 000000000000..0999fda978f4
Binary files /dev/null and b/modular_ss220/antagonists/icons/shadowlings/shadowlings_actions.dmi differ
diff --git a/modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi b/modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi
new file mode 100644
index 000000000000..4c822250f813
Binary files /dev/null and b/modular_ss220/antagonists/icons/shadowlings/shadowlings_clothes.dmi differ
diff --git a/modular_ss220/antagonists/icons/shadowlings/shadowlings_traps.dmi b/modular_ss220/antagonists/icons/shadowlings/shadowlings_traps.dmi
new file mode 100644
index 000000000000..f76d8bc62e98
Binary files /dev/null and b/modular_ss220/antagonists/icons/shadowlings/shadowlings_traps.dmi differ
diff --git a/modular_ss220/antagonists/sound/shadowlings/shadow_consumption_end.ogg b/modular_ss220/antagonists/sound/shadowlings/shadow_consumption_end.ogg
new file mode 100644
index 000000000000..06180446307d
Binary files /dev/null and b/modular_ss220/antagonists/sound/shadowlings/shadow_consumption_end.ogg differ
diff --git a/modular_ss220/antagonists/sound/shadowlings/shadow_consumption_start.ogg b/modular_ss220/antagonists/sound/shadowlings/shadow_consumption_start.ogg
new file mode 100644
index 000000000000..ccb6b85c211d
Binary files /dev/null and b/modular_ss220/antagonists/sound/shadowlings/shadow_consumption_start.ogg differ