diff --git a/_maps/templates/lazy_templates/heretic_sacrifice.dmm b/_maps/templates/lazy_templates/heretic_sacrifice.dmm
index d0277a1e712..cbabb4c6485 100644
--- a/_maps/templates/lazy_templates/heretic_sacrifice.dmm
+++ b/_maps/templates/lazy_templates/heretic_sacrifice.dmm
@@ -40,6 +40,14 @@
/obj/structure/cable,
/turf/open/floor/plating,
/area/centcom/heretic_sacrifice/rust)
+"fL" = (
+/obj/effect/decal/cleanable/blood/old,
+/obj/effect/decal/cleanable/dirt/dust,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
+"fO" = (
+/turf/open/indestructible,
+/area/space)
"gJ" = (
/obj/effect/decal/remains/human,
/turf/open/misc/dirt/jungle/dark{
@@ -47,6 +55,11 @@
slowdown = 0
},
/area/centcom/heretic_sacrifice/ash)
+"hE" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/storage/toolbox/mechanical/old,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"hZ" = (
/obj/effect/decal/cleanable/blood/old,
/turf/open/indestructible/necropolis/air,
@@ -56,6 +69,9 @@
/obj/structure/cable,
/turf/open/floor/plating/rust,
/area/centcom/heretic_sacrifice/rust)
+"jt" = (
+/turf/closed/indestructible/reinforced,
+/area/centcom/heretic_sacrifice/knock)
"jB" = (
/obj/machinery/light/very_dim/directional/south,
/turf/open/misc/asteroid,
@@ -85,6 +101,11 @@
},
/turf/open/floor/plating,
/area/centcom/heretic_sacrifice/rust)
+"mW" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/spear,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"mZ" = (
/obj/effect/decal/fakelattice{
density = 0
@@ -150,6 +171,12 @@
/obj/effect/decal/remains/human,
/turf/open/misc/asteroid,
/area/centcom/heretic_sacrifice/void)
+"qo" = (
+/obj/effect/decal/cleanable/blood/old,
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/storage/toolbox/mechanical/old,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"qu" = (
/obj/effect/turf_decal/weather/dirt,
/turf/open/misc/ashplanet/wateryrock{
@@ -202,6 +229,12 @@
},
/turf/open/floor/plating/rust,
/area/centcom/heretic_sacrifice/rust)
+"uM" = (
+/obj/effect/decal/cleanable/blood/old,
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/clothing/under/color/grey/ancient,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"uT" = (
/obj/structure/cable,
/turf/open/floor/plating/rust,
@@ -225,6 +258,9 @@
slowdown = 0
},
/area/centcom/heretic_sacrifice/ash)
+"wo" = (
+/turf/closed/indestructible/grille,
+/area/centcom/heretic_sacrifice/knock)
"wt" = (
/obj/structure/stone_tile/block{
dir = 1
@@ -240,6 +276,16 @@
slowdown = 0
},
/area/centcom/heretic_sacrifice/ash)
+"wP" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/flashlight/flare{
+ fuel = 1e+031;
+ randomize_fuel = 0;
+ icon_state = "flare-on";
+ on = 1
+ },
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"wS" = (
/turf/open/misc/dirt/jungle/dark{
planetary_atmos = 0;
@@ -252,6 +298,9 @@
name = "void glass floor"
},
/area/centcom/heretic_sacrifice/void)
+"xc" = (
+/turf/open/indestructible/white,
+/area/space)
"yC" = (
/obj/effect/turf_decal/trimline/brown/line{
dir = 1
@@ -273,6 +322,11 @@
},
/turf/open/floor/plating/rust,
/area/centcom/heretic_sacrifice/rust)
+"Aw" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/clothing/mask/gas,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"AH" = (
/obj/effect/turf_decal/trimline/brown/corner,
/turf/open/floor/plating/rust,
@@ -287,6 +341,15 @@
"AO" = (
/turf/open/floor/fakespace,
/area/centcom/heretic_sacrifice/void)
+"AW" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/clothing/mask/gas/tiki_mask/yalp_elor,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
+"AY" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"Bv" = (
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/trimline/brown/line,
@@ -340,6 +403,9 @@
slowdown = 0
},
/area/centcom/heretic_sacrifice/ash)
+"En" = (
+/turf/closed/indestructible/fakedoor/maintenance,
+/area/centcom/heretic_sacrifice/knock)
"ER" = (
/turf/open/misc/ironsand,
/area/centcom/heretic_sacrifice/rust)
@@ -467,6 +533,17 @@
/obj/machinery/light/very_dim/directional/west,
/turf/open/floor/fakespace,
/area/centcom/heretic_sacrifice/void)
+"MZ" = (
+/obj/effect/decal/cleanable/blood/old,
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/flashlight/flare{
+ fuel = 1e+031;
+ randomize_fuel = 0;
+ icon_state = "flare-on";
+ on = 1
+ },
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"Nh" = (
/obj/effect/turf_decal/trimline/brown/line{
dir = 1
@@ -508,6 +585,11 @@
slowdown = 0
},
/area/centcom/heretic_sacrifice/ash)
+"OW" = (
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/item/clothing/under/color/grey/ancient,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"Pl" = (
/obj/effect/turf_decal/weather/dirt{
dir = 6
@@ -627,6 +709,12 @@
/obj/effect/decal/cleanable/dirt,
/turf/open/indestructible/necropolis/air,
/area/centcom/heretic_sacrifice/flesh)
+"Zw" = (
+/obj/effect/decal/cleanable/blood/old,
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/effect/landmark/heretic/knock,
+/turf/open/indestructible/plating,
+/area/centcom/heretic_sacrifice/knock)
"ZA" = (
/turf/closed/indestructible/riveted/boss,
/area/centcom/heretic_sacrifice/ash)
@@ -661,9 +749,37 @@ ab
ab
ab
ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
"}
(2,1,1) = {"
ab
+ab
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
Fd
Fd
Fd
@@ -695,6 +811,20 @@ ab
"}
(3,1,1) = {"
ab
+ab
+Fd
+jt
+jt
+wo
+wo
+jt
+En
+En
+jt
+wo
+wo
+jt
+jt
Fd
ZA
ZA
@@ -726,6 +856,20 @@ ab
"}
(4,1,1) = {"
ab
+ab
+Fd
+jt
+AY
+AY
+AY
+AY
+AY
+AY
+AY
+AY
+fL
+AY
+jt
Fd
ZA
Pl
@@ -757,6 +901,20 @@ ab
"}
(5,1,1) = {"
ab
+ab
+Fd
+wo
+AY
+fL
+uM
+AY
+AY
+qo
+fL
+AY
+mW
+MZ
+wo
Fd
ZA
wS
@@ -788,6 +946,20 @@ ab
"}
(6,1,1) = {"
ab
+ab
+Fd
+wo
+AY
+Aw
+fL
+AY
+wP
+fL
+fL
+AY
+qo
+AY
+wo
Fd
ZA
OG
@@ -819,6 +991,20 @@ ab
"}
(7,1,1) = {"
ab
+ab
+Fd
+jt
+AY
+fL
+fL
+AY
+AY
+fL
+AY
+AY
+fL
+Aw
+jt
Fd
ZA
Bw
@@ -850,6 +1036,20 @@ ab
"}
(8,1,1) = {"
ab
+ab
+Fd
+En
+AY
+MZ
+fL
+AY
+Zw
+OW
+AY
+AY
+AY
+fL
+En
Fd
ZA
Bw
@@ -881,6 +1081,20 @@ ab
"}
(9,1,1) = {"
ab
+ab
+Fd
+En
+fL
+AY
+AY
+AY
+fL
+fL
+AY
+AY
+fL
+AY
+En
Fd
ZA
Bw
@@ -912,6 +1126,20 @@ ab
"}
(10,1,1) = {"
ab
+ab
+Fd
+jt
+AY
+AY
+fL
+AY
+fL
+AY
+fL
+AY
+AW
+fL
+jt
Fd
ZA
Bw
@@ -943,6 +1171,20 @@ ab
"}
(11,1,1) = {"
ab
+ab
+Fd
+wo
+fL
+AY
+hE
+MZ
+AY
+AY
+hE
+fL
+wP
+AY
+wo
Fd
ZA
Pl
@@ -974,6 +1216,20 @@ ab
"}
(12,1,1) = {"
ab
+ab
+Fd
+wo
+AY
+fL
+AY
+AY
+AY
+fL
+AY
+OW
+fL
+fL
+wo
Fd
ZA
wS
@@ -1005,6 +1261,20 @@ ab
"}
(13,1,1) = {"
ab
+ab
+Fd
+jt
+AY
+AY
+fL
+fL
+AY
+AY
+AY
+AY
+AY
+AY
+jt
Fd
ZA
OG
@@ -1036,6 +1306,20 @@ ab
"}
(14,1,1) = {"
ab
+ab
+Fd
+jt
+jt
+wo
+wo
+jt
+En
+En
+jt
+wo
+wo
+jt
+jt
Fd
ZA
ZA
@@ -1067,6 +1351,20 @@ ab
"}
(15,1,1) = {"
ab
+ab
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
Fd
Fd
Fd
@@ -1098,6 +1396,20 @@ ab
"}
(16,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
WD
@@ -1129,6 +1441,20 @@ ab
"}
(17,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+xc
+xc
+xc
+xc
+xc
+fO
Fd
WD
dZ
@@ -1160,6 +1486,20 @@ ab
"}
(18,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+xc
+fO
+fO
+fO
Fd
WD
dZ
@@ -1191,6 +1531,20 @@ ab
"}
(19,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+xc
+xc
+xc
+xc
+xc
+fO
Fd
WD
ER
@@ -1222,6 +1576,20 @@ ab
"}
(20,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
Se
@@ -1253,6 +1621,20 @@ ab
"}
(21,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+xc
+fO
+xc
+xc
+xc
+fO
Fd
WD
Xr
@@ -1284,6 +1666,20 @@ ab
"}
(22,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
Se
@@ -1315,6 +1711,20 @@ ab
"}
(23,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
Xr
@@ -1346,6 +1756,20 @@ ab
"}
(24,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
ps
@@ -1377,6 +1801,20 @@ ab
"}
(25,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
Je
@@ -1408,6 +1846,20 @@ ab
"}
(26,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
ER
@@ -1439,6 +1891,20 @@ ab
"}
(27,1,1) = {"
ab
+ab
+Fd
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
+fO
Fd
WD
WD
@@ -1470,6 +1936,20 @@ ab
"}
(28,1,1) = {"
ab
+ab
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
+Fd
Fd
Fd
Fd
@@ -1529,4 +2009,18 @@ ab
ab
ab
ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
+ab
"}
diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm
index b5efdb76f1a..4a0299d835a 100644
--- a/code/__DEFINES/achievements.dm
+++ b/code/__DEFINES/achievements.dm
@@ -46,6 +46,7 @@
#define MEDAL_VOID_ASCENSION "Void"
#define MEDAL_BLADE_ASCENSION "Blade"
#define MEDAL_COSMOS_ASCENSION "Cosmos"
+#define MEDAL_KNOCK_ASCENSION "Knock"
#define MEDAL_TOOLBOX_SOUL "Toolsoul"
#define MEDAL_CHEM_TUT "Beginner Chemist"
#define MEDAL_HOT_DAMN "Hot Damn!"
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 1f8a99d9d8c..d9d0cd5cf0d 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -80,6 +80,7 @@
#define PATH_VOID "Void Path"
#define PATH_BLADE "Blade Path"
#define PATH_COSMIC "Cosmic Path"
+#define PATH_KNOCK "Knock Path"
/// Defines are used in /proc/has_living_heart() to report if the heretic has no heart period, no living heart, or has a living heart.
#define HERETIC_NO_HEART_ORGAN -1
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm
index d47833f84c0..a2198cfb63c 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm
@@ -64,3 +64,6 @@
#define ATTACKER_SHOVING (1<<1)
/// The attack is a damaging-type attack
#define ATTACKER_DAMAGING_ATTACK (1<<2)
+
+/// Called on the atom being hit, from /datum/component/anti_magic/on_attack() : (obj/item/weapon, mob/user, antimagic_flags)
+#define COMSIG_ATOM_HOLYATTACK "atom_holyattacked"
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ade8a05798f..571f67f5b7d 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -1196,6 +1196,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_MAGNETIC_ID_CARD "magnetic_id_card"
/// ID cards with this trait have special appraisal text.
#define TRAIT_TASTEFULLY_THICK_ID_CARD "impressive_very_nice"
+/// things with this trait are treated as having no access in /obj/proc/check_access(obj/item)
+#define TRAIT_ALWAYS_NO_ACCESS "alwaysnoaccess"
/// Traits granted to items due to their chameleon properties.
#define CHAMELEON_ITEM_TRAIT "chameleon_item_trait"
diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm
index 309b31ceb37..a6cb2dedb32 100644
--- a/code/_globalvars/phobias.dm
+++ b/code/_globalvars/phobias.dm
@@ -366,8 +366,10 @@ GLOBAL_LIST_INIT(phobia_objs, list(
"heresy" = typecacheof(list(
/obj/effect/floating_blade,
/obj/effect/forcefield/cosmic_field,
- /obj/effect/heretic_rune,
+ /obj/effect/forcefield/wizard/heretic,
/obj/effect/heretic_influence,
+ /obj/effect/heretic_rune,
+ /obj/effect/knock_portal,
/obj/effect/visible_heretic_influence,
/obj/item/ammo_box/strilka310/lionhunter,
/obj/item/ammo_casing/strilka310/lionhunter,
@@ -379,6 +381,7 @@ GLOBAL_LIST_INIT(phobia_objs, list(
/obj/item/codex_cicatrix,
/obj/item/coin/eldritch,
/obj/item/gun/ballistic/rifle/lionhunter,
+ /obj/item/heretic_lintel,
/obj/item/melee/rune_carver,
/obj/item/melee/sickly_blade,
/obj/item/melee/touch_attack/mansus_fist,
@@ -387,6 +390,7 @@ GLOBAL_LIST_INIT(phobia_objs, list(
/obj/item/toy/reality_pierce,
/obj/projectile/curse_hand,
/obj/structure/destructible/eldritch_crucible,
+ /obj/structure/knock_tear,
)),
"insects" = typecacheof(list(
/obj/item/clothing/mask/animal/small/bee,
diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm
index 8240ebbf62e..f83f2cde7b0 100644
--- a/code/datums/achievements/misc_achievements.dm
+++ b/code/datums/achievements/misc_achievements.dm
@@ -135,6 +135,12 @@
database_id = MEDAL_COSMOS_ASCENSION
icon = "cosmicascend"
+/datum/award/achievement/misc/knock_ascension
+ name = "Secrets behind the Spider Door"
+ desc = "You managed to open a gate into the mansus."
+ database_id = MEDAL_KNOCK_ASCENSION
+ icon = "knockascend"
+
/datum/award/achievement/misc/grand_ritual_finale
name = "Archmage"
desc = "Made a big impression on the station with your phenomenal cosmic power."
diff --git a/code/datums/components/anti_magic.dm b/code/datums/components/anti_magic.dm
index d3d53237777..059bc2f787d 100644
--- a/code/datums/components/anti_magic.dm
+++ b/code/datums/components/anti_magic.dm
@@ -44,6 +44,7 @@
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
+ RegisterSignals(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_ATOM), PROC_REF(on_attack))
else if(ismob(parent))
register_antimagic_signals(parent)
else
@@ -131,3 +132,7 @@
return COMPONENT_MAGIC_BLOCKED
return NONE
+
+/datum/component/anti_magic/proc/on_attack(atom/movable/source, atom/target, mob/user)
+ SIGNAL_HANDLER
+ SEND_SIGNAL(target, COMSIG_ATOM_HOLYATTACK, source, user, antimagic_flags)
diff --git a/code/datums/elements/embed.dm b/code/datums/elements/embed.dm
index 0ba3a581d9a..1bc70dc500f 100644
--- a/code/datums/elements/embed.dm
+++ b/code/datums/elements/embed.dm
@@ -76,6 +76,9 @@
if(blocked || !istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE))
return FALSE
+
+ if(victim.status_flags & GODMODE)
+ return FALSE
var/flying_speed = throwingdatum?.speed || weapon.throw_speed
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index a69d5c183f4..4718471e87a 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -75,6 +75,7 @@
/obj/item/wirecutters,
/obj/item/wrench,
/obj/item/spess_knife,
+ /obj/item/melee/sickly_blade/knock,
))
/obj/item/storage/belt/utility/chief
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index 67f07bf6a75..7831fa1863c 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -124,6 +124,9 @@
/turf/open/indestructible/light
icon_state = "light_on-1"
+/turf/open/indestructible/plating
+ icon_state = "plating"
+
/turf/open/indestructible/permalube
icon_state = "darkfull"
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index ed6473548c9..e28f20dab36 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -64,6 +64,7 @@
PATH_VOID = "blue",
PATH_BLADE = "label", // my favorite color is label
PATH_COSMIC = "purple",
+ PATH_KNOCK = "yellow",
)
var/static/list/path_to_rune_color = list(
PATH_START = COLOR_LIME,
@@ -73,6 +74,7 @@
PATH_VOID = COLOR_CYAN,
PATH_BLADE = COLOR_SILVER,
PATH_COSMIC = COLOR_PURPLE,
+ PATH_KNOCK = COLOR_YELLOW,
)
/datum/antagonist/heretic/Destroy()
@@ -508,6 +510,7 @@
.["Remove Heart Target"] = CALLBACK(src, PROC_REF(remove_target))
.["Adjust Knowledge Points"] = CALLBACK(src, PROC_REF(admin_change_points))
+ .["Give Focus"] = CALLBACK(src, PROC_REF(admin_give_focus))
/**
* Admin proc for giving a heretic a Living Heart easily.
@@ -583,6 +586,18 @@
knowledge_points += change_num
+/**
+ * Admin proc for giving a heretic a focus.
+ */
+/datum/antagonist/heretic/proc/admin_give_focus(mob/admin)
+ if(!admin.client?.holder)
+ to_chat(admin, span_warning("You shouldn't be using this!"))
+ return
+
+ var/mob/living/pawn = owner.current
+ pawn.equip_to_slot_if_possible(new /obj/item/clothing/neck/heretic_focus(get_turf(pawn)), ITEM_SLOT_NECK, TRUE, TRUE)
+ to_chat(pawn, span_hypnophrase("The Mansus has manifested you a focus."))
+
/datum/antagonist/heretic/antag_panel_data()
var/list/string_of_knowledge = list()
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index c074e13c8d3..63493a5dc4b 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -115,3 +115,14 @@
icon_state = "cosmic_blade"
inhand_icon_state = "cosmic_blade"
after_use_message = "The Stargazer hears your call..."
+
+// Path of Knock's blade
+/obj/item/melee/sickly_blade/knock
+ name = "\improper key blade"
+ desc = "A blade and a key, a key to what? \
+ What grand gates does it open?"
+ icon_state = "key_blade"
+ inhand_icon_state = "key_blade"
+ after_use_message = "The Mother of Ants hears your call..."
+ tool_behaviour = TOOL_CROWBAR
+ toolspeed = 1.3
diff --git a/code/modules/antagonists/heretic/items/keyring.dm b/code/modules/antagonists/heretic/items/keyring.dm
new file mode 100644
index 00000000000..0498ba9e8a2
--- /dev/null
+++ b/code/modules/antagonists/heretic/items/keyring.dm
@@ -0,0 +1,186 @@
+/obj/effect/knock_portal
+ name = "crack in reality"
+ desc = "A crack in space, impossibly deep and painful to the eyes. Definitely not safe."
+ icon = 'icons/effects/eldritch.dmi'
+ icon_state = "realitycrack"
+ light_system = STATIC_LIGHT
+ light_power = 1
+ light_on = TRUE
+ light_color = COLOR_GREEN
+ light_range = 3
+ opacity = TRUE
+ density = FALSE //so we dont block doors closing
+ layer = OBJ_LAYER //under doors
+ ///The knock portal we teleport to
+ var/obj/effect/knock_portal/destination
+ ///The airlock we are linked to, we delete if it is destroyed
+ var/obj/machinery/door/our_airlock
+
+/obj/effect/knock_portal/Initialize(mapload, target)
+ . = ..()
+ if(target)
+ our_airlock = target
+ RegisterSignal(target, COMSIG_QDELETING, PROC_REF(delete_on_door_delete))
+
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
+///Deletes us and our destination portal if our_airlock is destroyed
+/obj/effect/knock_portal/proc/delete_on_door_delete(datum/source)
+ SIGNAL_HANDLER
+ qdel(src)
+
+///Signal handler for when our location is entered, calls teleport on the victim, if their old_loc didnt contain a portal already (to prevent loops)
+/obj/effect/knock_portal/proc/on_entered(datum/source, mob/living/loser, atom/old_loc)
+ SIGNAL_HANDLER
+ if(istype(loser) && !(locate(type) in old_loc))
+ teleport(loser)
+
+/obj/effect/knock_portal/Destroy()
+ QDEL_NULL(destination)
+ our_airlock = null
+ return ..()
+
+///Teleports the teleportee, to a random airlock if the teleportee isnt a heretic, or the other portal if they are one
+/obj/effect/knock_portal/proc/teleport(mob/living/teleportee)
+ if(isnull(destination)) //dumbass
+ qdel(src)
+ return
+
+ //get it?
+ var/obj/machinery/door/doorstination = IS_HERETIC_OR_MONSTER(teleportee) ? destination.our_airlock : find_random_airlock()
+ if(!do_teleport(teleportee, get_turf(doorstination), channel = TELEPORT_CHANNEL_MAGIC))
+ return
+
+ if(!IS_HERETIC_OR_MONSTER(teleportee))
+ teleportee.apply_damage(20, BRUTE) //so they dont roll it like a jackpot machine to see if they can land in the armory
+ to_chat(teleportee, span_userdanger("You stumble through [src], battered by forces beyond your comprehension, landing anywhere but where you thought you were going."))
+
+ INVOKE_ASYNC(src, PROC_REF(async_opendoor), doorstination)
+
+///Returns a random airlock on the same Z level as our portal, that isnt our airlock
+/obj/effect/knock_portal/proc/find_random_airlock()
+ var/list/turf/possible_destinations = list()
+ for(var/obj/airlock as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/airlock))
+ if(airlock.z != z)
+ continue
+ if(airlock.loc == loc)
+ continue
+ possible_destinations += airlock
+ return pick(possible_destinations)
+
+///Asynchronous proc to unbolt, then open the passed door
+/obj/effect/knock_portal/proc/async_opendoor(obj/machinery/door/door)
+ if(istype(door, /obj/machinery/door/airlock)) //they can create portals on ANY door, but we should unlock airlocks so they can actually open
+ var/obj/machinery/door/airlock/as_airlock = door
+ as_airlock.unbolt()
+ door.open()
+
+///An ID card capable of shapeshifting to other IDs given by the Key Keepers Burden knowledge
+/obj/item/card/id/advanced/heretic
+ ///List of IDs this card consumed
+ var/list/obj/item/card/id/fused_ids = list()
+ ///The first portal in the portal pair, so we can clear it later
+ var/obj/effect/knock_portal/portal_one
+ ///The second portal in the portal pair, so we can clear it later
+ var/obj/effect/knock_portal/portal_two
+ ///The first door we are linking in the pair, so we can create a portal pair
+ var/datum/weakref/link
+
+/obj/item/card/id/advanced/heretic/examine(mob/user)
+ . = ..()
+ if(!IS_HERETIC_OR_MONSTER(user))
+ return
+ . += span_hypnophrase("Enchanted by the Mansus!")
+ . += span_hypnophrase("Using an ID on this will consume it and allow you to copy its accesses.")
+ . += span_hypnophrase("Using this in-hand allows you to change its appearance.")
+ . += span_hypnophrase("Using this on a pair of doors, allows you to link them together. Entering one door will transport you to the other, while heathens are instead teleported to a random airlock.")
+
+/obj/item/card/id/advanced/heretic/attack_self(mob/user)
+ . = ..()
+ if(!IS_HERETIC(user))
+ return
+ var/cardname = tgui_input_list(user, "Shapeshift into?", "Shapeshift", fused_ids)
+ if(!cardname)
+ balloon_alert(user, "no options!")
+ return ..()
+ var/obj/item/card/id/card = fused_ids[cardname]
+ shapeshift(card)
+
+///Changes our appearance to the passed ID card
+/obj/item/card/id/advanced/heretic/proc/shapeshift(obj/item/card/id/advanced/card)
+ trim = card.trim
+ assignment = card.assignment
+ registered_age = card.registered_age
+ registered_name = card.registered_name
+ icon_state = card.icon_state
+ inhand_icon_state = card.inhand_icon_state
+ assigned_icon_state = card.assigned_icon_state
+ name = card.name //not update_label because of the captains spare moment
+ update_icon()
+
+///Deletes and nulls our portal pair
+/obj/item/card/id/advanced/heretic/proc/clear_portals()
+ QDEL_NULL(portal_one)
+ QDEL_NULL(portal_two)
+
+///Clears portal references
+/obj/item/card/id/advanced/heretic/proc/clear_portal_refs()
+ SIGNAL_HANDLER
+ portal_one = null
+ portal_two = null
+
+///Creates a portal pair at door1 and door2, displays a balloon alert to user
+/obj/item/card/id/advanced/heretic/proc/make_portal(mob/user, obj/machinery/door/door1, obj/machinery/door/door2)
+ var/message = "linked"
+ if(portal_one || portal_two)
+ clear_portals()
+ message += ", previous cleared"
+
+ portal_one = new(get_turf(door2), door2)
+ portal_two = new(get_turf(door1), door1)
+ portal_one.destination = portal_two
+ RegisterSignal(portal_one, COMSIG_QDELETING, PROC_REF(clear_portal_refs)) //we only really need to register one because they already qdel both portals if one is destroyed
+ portal_two.destination = portal_one
+ balloon_alert(user, "[message]")
+
+/obj/item/card/id/advanced/heretic/attackby(obj/item/thing, mob/user, params)
+ if(!istype(thing, /obj/item/card/id/advanced) || !IS_HERETIC(user))
+ return ..()
+ var/obj/item/card/id/card = thing
+ fused_ids[card.name] = card
+ card.moveToNullspace()
+ playsound(drop_location(),'sound/items/eatfood.ogg', rand(10,50), TRUE)
+ access += card.access
+
+/obj/item/card/id/advanced/heretic/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!proximity_flag || !IS_HERETIC(user))
+ return
+ if(istype(target, /obj/effect/knock_portal))
+ clear_portals()
+ return
+
+ if(!istype(target, /obj/machinery/door))
+ return
+
+ var/reference_resolved = link?.resolve()
+ if(reference_resolved == target)
+ return
+
+ if(reference_resolved)
+ make_portal(user, reference_resolved, target)
+ to_chat(user, span_notice("You use [src], to link [link] and [target] together."))
+ link = null
+ balloon_alert(user, "link 2/2")
+ else
+ link = WEAKREF(target)
+ balloon_alert(user, "link 1/2")
+
+/obj/item/card/id/advanced/heretic/Destroy()
+ QDEL_LIST_ASSOC(fused_ids)
+ link = null
+ clear_portals()
+ return ..()
diff --git a/code/modules/antagonists/heretic/items/lintel.dm b/code/modules/antagonists/heretic/items/lintel.dm
new file mode 100644
index 00000000000..140453842c0
--- /dev/null
+++ b/code/modules/antagonists/heretic/items/lintel.dm
@@ -0,0 +1,64 @@
+/obj/effect/forcefield/wizard/heretic
+ name = "consecrated lintel"
+ desc = "A field of papers flying in the air, repulsing heathens with impossible force."
+ icon_state = "lintel"
+ initial_duration = 8 SECONDS
+
+/obj/effect/forcefield/wizard/heretic/Bumped(mob/living/bumpee)
+ . = ..()
+ if(!istype(bumpee) || IS_HERETIC_OR_MONSTER(bumpee))
+ return
+ var/throwtarget = get_edge_target_turf(loc, get_dir(loc, get_step_away(bumpee, loc)))
+ bumpee.safe_throw_at(throwtarget, 10, 1, force = MOVE_FORCE_EXTREMELY_STRONG)
+ visible_message(span_danger("[src] repulses [bumpee] in a storm of paper!"))
+
+///A heretic item that spawns a barrier at the clicked turf, 3 uses
+/obj/item/heretic_lintel
+ name = "consecrated book"
+ desc = "Some kind of book, its contents make your head hurt. The material is not known to you and it seems to shift and twist unnaturally."
+ icon = 'icons/obj/service/library.dmi'
+ icon_state = "hereticlintel"
+ force = 10
+ damtype = BURN
+ worn_icon_state = "book"
+ throw_speed = 1
+ throw_range = 5
+ w_class = WEIGHT_CLASS_NORMAL
+ attack_verb_continuous = list("bashes", "curses")
+ attack_verb_simple = list("bash", "curse")
+ resistance_flags = FLAMMABLE
+ drop_sound = 'sound/items/handling/book_drop.ogg'
+ pickup_sound = 'sound/items/handling/book_pickup.ogg'
+ ///what type of barrier do we spawn when used
+ var/barrier_type = /obj/effect/forcefield/wizard/heretic
+ ///how many uses do we have left
+ var/uses = 3
+
+/obj/item/heretic_lintel/examine(mob/user)
+ . = ..()
+ if(!IS_HERETIC_OR_MONSTER(user))
+ return
+ . += span_hypnophrase("Materializes a barrier upon any tile in sight, which only you can pass through. Lasts 8 seconds.")
+ . += span_hypnophrase("It has [uses] uses left.")
+
+/obj/item/heretic_lintel/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
+ if(IS_HERETIC(user))
+ var/turf/turf_target = get_turf(target)
+ if(locate(barrier_type) in turf_target)
+ user.balloon_alert(user, "already occupied!")
+ return
+ turf_target.visible_message(span_warning("A storm of paper materializes!"))
+ new /obj/effect/temp_visual/paper_scatter(turf_target)
+ playsound(turf_target, 'sound/magic/smoke.ogg', 30)
+ new barrier_type(turf_target, user)
+ uses--
+ if(uses <= 0)
+ to_chat(user, span_warning("[src] falls apart, turning into ash and dust!"))
+ qdel(src)
+ return
+ var/mob/living/carbon/human/human_user = user
+ to_chat(human_user, span_userdanger("Your mind burns as you stare deep into the book, a headache setting in like your brain is on fire!"))
+ human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 30, 190)
+ human_user.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ human_user.dropItemToGround(src)
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index c688e285735..33253220b22 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -262,6 +262,8 @@
I finally began to understand. And then, blood rained from the heavens."
next_knowledge = list(/datum/heretic_knowledge/summon/stalker)
route = PATH_FLESH
+ ///What type of wound do we apply on hit
+ var/wound_type = /datum/wound/slash/flesh/severe
/datum/heretic_knowledge/blade_upgrade/flesh/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
if(!iscarbon(target) || source == target)
@@ -269,7 +271,8 @@
var/mob/living/carbon/carbon_target = target
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
- carbon_target.cause_wound_of_type_and_severity(WOUND_SLASH, bodypart, WOUND_SEVERITY_SEVERE, WOUND_SEVERITY_CRITICAL)
+ var/datum/wound/crit_wound = new wound_type()
+ crit_wound.apply_wound(bodypart, attack_direction = get_dir(source, target))
/datum/heretic_knowledge/summon/stalker
name = "Lonely Ritual"
diff --git a/code/modules/antagonists/heretic/knowledge/knock_lore.dm b/code/modules/antagonists/heretic/knowledge/knock_lore.dm
new file mode 100644
index 00000000000..fcb2c6ceb4c
--- /dev/null
+++ b/code/modules/antagonists/heretic/knowledge/knock_lore.dm
@@ -0,0 +1,227 @@
+/**
+ * # The path of Knock.
+ *
+ * Goes as follows:
+ *
+ * A Locksmith’s Secret
+ * Grasp of Knock
+ * > Sidepaths:
+ * Ashen Eyes
+ * Codex Cicatrix
+ * Key Keeper’s Burden
+ *
+ * Rite Of Passage
+ * Mark Of Knock
+ * Ritual of Knowledge
+ * Burglar's Finesse
+ * > Sidepaths:
+ * Apetra Vulnera
+ * Opening Blast
+ *
+ * Opening Blade
+ * Caretaker’s Last Refuge
+ *
+ * Many secrets behind the Spider Door
+ */
+/datum/heretic_knowledge/limited_amount/starting/base_knock
+ name = "A Locksmith’s Secret"
+ desc = "Opens up the Path of Knock to you. \
+ Allows you to transmute a knife and a crowbar into a Key Blade. \
+ You can only create two at a time and they function as fast crowbars. \
+ In addition, they can fit into utility belts."
+ gain_text = "The Knock permits no seal and no isolation. It thrusts us gleefully out of the safety of ignorance."
+ next_knowledge = list(/datum/heretic_knowledge/knock_grasp)
+ required_atoms = list(
+ /obj/item/knife = 1,
+ /obj/item/crowbar = 1,
+ )
+ result_atoms = list(/obj/item/melee/sickly_blade/knock)
+ limit = 2
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/knock_grasp
+ name = "Grasp of Knock"
+ desc = "Your mansus grasp allows you to access anything! Right click on an airlock or a locker to force it open. \
+ DNA locks on mechs will be removed, and any pilot will be ejected. Works on consoles. \
+ Makes a distinctive knocking sound on use."
+ gain_text = "Nothing may remain closed from my touch."
+ next_knowledge = list(
+ /datum/heretic_knowledge/key_ring,
+ /datum/heretic_knowledge/medallion,
+ /datum/heretic_knowledge/codex_cicatrix,
+ )
+ cost = 1
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/knock_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
+ RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK_SECONDARY, PROC_REF(on_secondary_mansus_grasp))
+ RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
+
+/datum/heretic_knowledge/knock_grasp/on_lose(mob/user, datum/antagonist/heretic/our_heretic)
+ UnregisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK_SECONDARY)
+ UnregisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK)
+
+/datum/heretic_knowledge/knock_grasp/proc/on_mansus_grasp(mob/living/source, mob/living/target)
+ SIGNAL_HANDLER
+ var/obj/item/clothing/under/suit = target.get_item_by_slot(ITEM_SLOT_ICLOTHING)
+ if(istype(suit) && suit.adjusted == NORMAL_STYLE)
+ suit.toggle_jumpsuit_adjust()
+ suit.update_appearance()
+
+/datum/heretic_knowledge/knock_grasp/proc/on_secondary_mansus_grasp(mob/living/source, atom/target)
+ SIGNAL_HANDLER
+
+ if(ismecha(target))
+ var/obj/vehicle/sealed/mecha/mecha = target
+ mecha.dna_lock = null
+ for(var/mob/living/occupant as anything in mecha.occupants)
+ if(isAI(occupant))
+ continue
+ mecha.mob_exit(occupant, randomstep = TRUE)
+ else if(istype(target,/obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/door = target
+ door.unbolt()
+ else if(istype(target, /obj/machinery/computer))
+ var/obj/machinery/computer/computer = target
+ computer.authenticated = TRUE
+ computer.balloon_alert(source, "unlocked")
+
+ var/turf/target_turf = get_turf(target)
+ SEND_SIGNAL(target_turf, COMSIG_ATOM_MAGICALLY_UNLOCKED, src, source)
+ playsound(target, 'sound/magic/hereticknock.ogg', 100, TRUE, -1)
+
+ return COMPONENT_USE_HAND
+
+/datum/heretic_knowledge/key_ring
+ name = "Key Keeper’s Burden"
+ desc = "Allows you to transmute a wallet, an iron rod, and an ID card to create an Eldritch Card. \
+ It functions the same as an ID Card, but attacking it with an ID card fuses it and gains its access. \
+ You can use it in-hand to change its form to a card you fused. \
+ Does not preserve the card used in the ritual."
+ gain_text = "Gateways shall open before me, my very will ensnaring reality."
+ required_atoms = list(
+ /obj/item/storage/wallet = 1,
+ /obj/item/stack/rods = 1,
+ /obj/item/card/id = 1,
+ )
+ result_atoms = list(/obj/item/card/id/advanced/heretic)
+ next_knowledge = list(/datum/heretic_knowledge/limited_amount/rite_of_passage)
+ cost = 1
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/limited_amount/rite_of_passage // item that creates 3 max at a time heretic only barriers, probably should limit to 1 only, holy people can also pass
+ name = "Rite Of Passage"
+ desc = "Allows you to transmute a white crayon, a wooden plank, and a multitool to create a Consecrated Book. \
+ It can materialize a barricade at range that only you and people resistant to magic can pass. 3 uses."
+ gain_text = "With this I can repel those that intend me harm."
+ required_atoms = list(
+ /obj/item/toy/crayon/white = 1,
+ /obj/item/stack/sheet/mineral/wood = 1,
+ /obj/item/multitool = 1,
+ )
+ result_atoms = list(/obj/item/heretic_lintel)
+ next_knowledge = list(/datum/heretic_knowledge/mark/knock_mark)
+ cost = 1
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/mark/knock_mark
+ name = "Mark of Knock"
+ desc = "Your Mansus Grasp now applies the Mark of Knock. \
+ Attack a marked person to bar them from all passages for the duration of the mark. \
+ This will make it so that they have no access whatsoever, even public access doors will reject them."
+ gain_text = "Their requests for passage will remain unheeded."
+ next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/knock)
+ route = PATH_KNOCK
+ mark_type = /datum/status_effect/eldritch/knock
+
+/datum/heretic_knowledge/knowledge_ritual/knock
+ next_knowledge = list(/datum/heretic_knowledge/spell/burglar_finesse)
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/spell/burglar_finesse
+ name = "Burglar's Finesse"
+ desc = "Grants you Burglar's Finesse, a single-target spell \
+ that puts a random item from the victims backpack into your hand."
+ gain_text = "Their trinkets will be mine, as will their lives in due time."
+ next_knowledge = list(
+ /datum/heretic_knowledge/spell/apetra_vulnera,
+ /datum/heretic_knowledge/spell/opening_blast,
+ /datum/heretic_knowledge/blade_upgrade/flesh/knock,
+ )
+ spell_to_add = /datum/action/cooldown/spell/pointed/burglar_finesse
+ cost = 2
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/blade_upgrade/flesh/knock //basically a chance-based weeping avulsion version of the former
+ name = "Opening Blade"
+ desc = "Your blade has a chance to cause a weeping avulsion on attack."
+ gain_text = "The power of my patron courses through my blade, willing their very flesh to part."
+ next_knowledge = list(/datum/heretic_knowledge/spell/caretaker_refuge)
+ route = PATH_KNOCK
+ wound_type = /datum/wound/slash/flesh/critical
+ var/chance = 35
+
+/datum/heretic_knowledge/blade_upgrade/flesh/knock/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
+ if(prob(chance))
+ return ..()
+
+/datum/heretic_knowledge/spell/caretaker_refuge
+ name = "Caretaker’s Last Refuge"
+ desc = "Gives you a spell that makes you transparent and not dense. Cannot be used near living sentient beings. \
+ While in refuge, you cannot use your hands or spells, and you are immune to slowdown. \
+ You are invincible but unable to harm anything. Cancelled by being hit with an anti-magic item."
+ gain_text = "Then I saw my my own reflection cascaded mind-numbingly enough times that I was but a haze."
+ next_knowledge = list(/datum/heretic_knowledge/ultimate/knock_final)
+ route = PATH_KNOCK
+ spell_to_add = /datum/action/cooldown/spell/caretaker
+ cost = 1
+
+/datum/heretic_knowledge/ultimate/knock_final
+ name = "Many secrets behind the Spider Door"
+ desc = "The ascension ritual of the Path of Knock. \
+ Bring 3 corpses without organs in their torso to a transmutation rune to complete the ritual. \
+ When completed, you gain the ability to transform into empowered eldritch creatures \
+ and in addition, create a tear to the Spider Door; \
+ a tear in reality located at the site of this ritual. \
+ Eldritch creatures will endlessly pour from this rift \
+ who are bound to obey your instructions."
+ gain_text = "With her knowledge, and what I had seen, I knew what to do. \
+ I had to open the gates, with the holes in my foes as Ways! \
+ Reality will soon be torn, the Spider Gate opened! WITNESS ME!"
+ required_atoms = list(/mob/living/carbon/human = 3)
+ route = PATH_KNOCK
+
+/datum/heretic_knowledge/ultimate/knock_final/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ for(var/mob/living/carbon/human/body in atoms)
+ if(body.stat != DEAD)
+ continue
+ var/obj/item/bodypart/chest = body.get_bodypart(BODY_ZONE_CHEST)
+ if(LAZYLEN(chest.get_organs()))
+ to_chat(user, span_hierophant_warning("[body] has organs in their chest."))
+ continue
+
+ selected_atoms += body
+
+ if(!LAZYLEN(selected_atoms))
+ loc.balloon_alert(user, "ritual failed, not enough valid bodies!")
+ return FALSE
+ return TRUE
+
+/datum/heretic_knowledge/ultimate/knock_final/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
+ . = ..()
+ priority_announce("Delta-class dimensional anomaly detec[generate_heretic_text()] Reality rended, torn. Gates open, doors open, [user.real_name] has ascended! Fear the tide! [generate_heretic_text()]", "Centra[generate_heretic_text()]", ANNOUNCER_SPANOMALIES)
+ user.client?.give_award(/datum/award/achievement/misc/knock_ascension, user)
+
+ // buffs
+ var/datum/action/cooldown/spell/shapeshift/eldritch/ascension/transform_spell = new(user.mind)
+ transform_spell.Grant(user)
+
+ user.client?.give_award(/datum/award/achievement/misc/knock_ascension, user)
+ var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/heretic_knowledge/blade_upgrade/flesh/knock/blade_upgrade = heretic_datum.get_knowledge(/datum/heretic_knowledge/blade_upgrade/flesh/knock)
+ blade_upgrade.chance += 30
+ new /obj/structure/knock_tear(loc, user.mind)
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
index 942b053ae67..983bbee32c6 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
@@ -34,6 +34,10 @@ GLOBAL_LIST_EMPTY(heretic_sacrifice_landmarks)
name = "rust heretic sacrifice landmark"
for_heretic_path = PATH_RUST
+/obj/effect/landmark/heretic/knock
+ name = "knock heretic sacrifice landmark"
+ for_heretic_path = PATH_KNOCK
+
// A fluff signpost object that doesn't teleport you somewhere when you touch it.
/obj/structure/no_effect_signpost
name = "signpost"
@@ -106,3 +110,8 @@ GLOBAL_LIST_EMPTY(heretic_sacrifice_landmarks)
name = "Mansus Rust Gate"
ambience_index = AMBIENCE_REEBE
sound_environment = SOUND_ENVIRONMENT_SEWER_PIPE
+
+/area/centcom/heretic_sacrifice/knock
+ name = "Mansus Knock Gate"
+ ambience_index = AMBIENCE_DANGER
+ sound_environment = SOUND_ENVIRONMENT_PSYCHOTIC
diff --git a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
index 4a315575d61..6439840fed5 100644
--- a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
@@ -26,6 +26,7 @@
Also has a chance to transfer wounds from you to the victim."
gain_text = "\"No matter the man, we bleed all the same.\" That's what the Marshal told me."
next_knowledge = list(
+ /datum/heretic_knowledge/spell/apetra_vulnera,
/datum/heretic_knowledge/spell/void_phase,
/datum/heretic_knowledge/summon/raw_prophet,
)
diff --git a/code/modules/antagonists/heretic/knowledge/side_knock_flesh.dm b/code/modules/antagonists/heretic/knowledge/side_knock_flesh.dm
new file mode 100644
index 00000000000..97218ce5e94
--- /dev/null
+++ b/code/modules/antagonists/heretic/knowledge/side_knock_flesh.dm
@@ -0,0 +1,28 @@
+// Sidepaths for knowledge between Knock and Flesh.
+
+/datum/heretic_knowledge/spell/apetra_vulnera
+ name = "Apetra Vulnera"
+ desc = "Grants you Apetra Vulnera, a spell \
+ which causes heavy bleeding on all bodyparts of the victim that have more than 15 brute damage. \
+ Wounds a random limb if no limb is sufficiently damaged."
+ gain_text = "Flesh opens, and blood spills. My master seeks sacrifice, and I shall appease."
+ next_knowledge = list(
+ /datum/heretic_knowledge/spell/blood_siphon,
+ /datum/heretic_knowledge/void_cloak,
+ )
+ spell_to_add = /datum/action/cooldown/spell/pointed/apetra_vulnera
+ cost = 1
+ route = PATH_SIDE
+
+/datum/heretic_knowledge/spell/opening_blast
+ name = "Wave Of Desperation"
+ desc = "Grants you Wave Of Desparation, a spell which can only be cast while restrained. \
+ It removes your restraints, repels and knocks down adjacent people, and applies the Mansus Grasp to everything nearby."
+ gain_text = "My shackles undone in dark fury, their feeble bindings crumble before my power."
+ next_knowledge = list(
+ /datum/heretic_knowledge/summon/ashy,
+ /datum/heretic_knowledge/void_cloak,
+ )
+ spell_to_add = /datum/action/cooldown/spell/aoe/wave_of_desperation
+ cost = 1
+ route = PATH_SIDE
diff --git a/code/modules/antagonists/heretic/magic/apetravulnera.dm b/code/modules/antagonists/heretic/magic/apetravulnera.dm
new file mode 100644
index 00000000000..801104dddf9
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/apetravulnera.dm
@@ -0,0 +1,59 @@
+/datum/action/cooldown/spell/pointed/apetra_vulnera
+ name = "Apetra Vulnera"
+ desc = "Causes severe bleeding on every limb of a target which has more than 15 brute damage. \
+ Wounds a random limb if no limb is sufficiently damaged."
+ background_icon_state = "bg_heretic"
+ overlay_icon_state = "bg_heretic_border"
+ button_icon = 'icons/mob/actions/actions_ecult.dmi'
+ button_icon_state = "cleave"
+
+ school = SCHOOL_FORBIDDEN
+ cooldown_time = 45 SECONDS
+
+ invocation = "AP'TRA VULN'RA!"
+ invocation_type = INVOCATION_WHISPER
+ spell_requirements = NONE
+
+ cast_range = 4
+ /// What type of wound we apply
+ var/wound_type = /datum/wound/slash/flesh/critical/cleave
+
+/datum/action/cooldown/spell/pointed/apetra_vulnera/is_valid_target(atom/cast_on)
+ return ..() && ishuman(cast_on)
+
+/datum/action/cooldown/spell/pointed/apetra_vulnera/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+
+ if(IS_HERETIC_OR_MONSTER(cast_on))
+ return FALSE
+
+ if(!cast_on.blood_volume)
+ return FALSE
+
+ if(cast_on.can_block_magic(antimagic_flags))
+ cast_on.visible_message(
+ span_danger("[cast_on]'s bruises briefly glow, but repels the effect!"),
+ span_danger("Your bruises sting a little, but you are protected!")
+ )
+ return FALSE
+
+ var/a_limb_got_damaged = FALSE
+ for(var/obj/item/bodypart/bodypart in cast_on.bodyparts)
+ if(bodypart.brute_dam < 15)
+ continue
+ a_limb_got_damaged = TRUE
+ var/datum/wound/slash/crit_wound = new wound_type()
+ crit_wound.apply_wound(bodypart)
+
+ if(!a_limb_got_damaged)
+ var/datum/wound/slash/crit_wound = new wound_type()
+ crit_wound.apply_wound(pick(cast_on.bodyparts))
+
+ cast_on.visible_message(
+ span_danger("[cast_on]'s scratches and bruises are torn open by an unholy force!"),
+ span_danger("Your scratches and bruises are torn open by some horrible unholy force!")
+ )
+
+ new /obj/effect/temp_visual/cleave(get_turf(cast_on))
+
+ return TRUE
diff --git a/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm b/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm
new file mode 100644
index 00000000000..4395b4a54b3
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm
@@ -0,0 +1,32 @@
+// Given to ascended knock heretics, is a form of shapeshift that can turn into all 4 common heretic summons, and is not limited to 1 selection.
+/datum/action/cooldown/spell/shapeshift/eldritch/ascension
+ name = "Ascended Shapechange"
+ desc = "A spell that allows you to take on the form of another eldritch creature, gaining their abilities. \
+ You can change your choice at any time, and if your form dies, you dont die."
+ cooldown_time = 20 SECONDS
+ die_with_shapeshifted_form = FALSE
+ possible_shapes = list(
+ /mob/living/simple_animal/hostile/heretic_summon/raw_prophet,
+ /mob/living/simple_animal/hostile/heretic_summon/rust_spirit,
+ /mob/living/simple_animal/hostile/heretic_summon/ash_spirit,
+ /mob/living/simple_animal/hostile/heretic_summon/stalker,
+ )
+
+/datum/action/cooldown/spell/shapeshift/eldritch/ascension/do_shapeshift(mob/living/caster)
+ . = ..()
+ if(!.)
+ return
+ //buff our forms so this ascension ability isnt shit
+ playsound(caster, 'sound/magic/demon_consume.ogg', 50, TRUE)
+ var/mob/living/monster = .
+ monster.AddComponent(/datum/component/seethrough_mob)
+ monster.maxHealth *= 1.5
+ monster.health = monster.maxHealth
+ monster.melee_damage_lower = max((monster.melee_damage_lower * 2), 40)
+ monster.melee_damage_upper = monster.melee_damage_upper / 2
+ monster.transform *= 1.5
+ monster.AddElement(/datum/element/wall_smasher, strength_flag = ENVIRONMENT_SMASH_RWALLS)
+
+/datum/action/cooldown/spell/shapeshift/eldritch/ascension/do_unshapeshift(mob/living/caster)
+ . = ..()
+ shapeshift_type = null //pick another loser
diff --git a/code/modules/antagonists/heretic/magic/burglar_finesse.dm b/code/modules/antagonists/heretic/magic/burglar_finesse.dm
new file mode 100644
index 00000000000..7bb6960354e
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/burglar_finesse.dm
@@ -0,0 +1,39 @@
+/datum/action/cooldown/spell/pointed/burglar_finesse
+ name = "Burglar's Finesse"
+ desc = "Steal a random item from the victim's backpack."
+ background_icon_state = "bg_heretic"
+ overlay_icon_state = "bg_heretic_border"
+ button_icon = 'icons/mob/actions/actions_ecult.dmi'
+ button_icon_state = "burglarsfinesse"
+
+ school = SCHOOL_FORBIDDEN
+ cooldown_time = 40 SECONDS
+
+ invocation = "Y'O'K!"
+ invocation_type = INVOCATION_WHISPER
+ spell_requirements = NONE
+
+ cast_range = 4
+
+/datum/action/cooldown/spell/pointed/burglar_finesse/is_valid_target(atom/cast_on)
+ return ..() && ishuman(cast_on) && (locate(/obj/item/storage/backpack) in cast_on.contents)
+
+/datum/action/cooldown/spell/pointed/burglar_finesse/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+ if(cast_on.can_block_magic(antimagic_flags))
+ to_chat(cast_on, span_danger("You feel a light tug, but are otherwise fine, you were protected by holiness!"))
+ to_chat(owner, span_danger("[cast_on] is protected by holy forces!"))
+ return FALSE
+
+ var/obj/storage_item = locate(/obj/item/storage/backpack) in cast_on.contents
+
+ if(isnull(storage_item))
+ return FALSE
+
+ var/item = pick(storage_item.contents)
+ if(isnull(item))
+ return FALSE
+
+ to_chat(cast_on, span_warning("Your [storage_item] feels lighter..."))
+ to_chat(owner, span_notice("With a blink, you pull [item] out of [cast_on][p_s()] [storage_item]."))
+ owner.put_in_active_hand(item)
diff --git a/code/modules/antagonists/heretic/magic/caretaker.dm b/code/modules/antagonists/heretic/magic/caretaker.dm
new file mode 100644
index 00000000000..87f3a69dad1
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/caretaker.dm
@@ -0,0 +1,39 @@
+/datum/action/cooldown/spell/caretaker
+ name = "Caretaker’s Last Refuge"
+ desc = "Shifts you into the Caretaker's Refuge, rendering you translucent and intangible. \
+ While in the Refuge your movement is unrestricted, but you cannot use your hands or cast any spells. \
+ You cannot enter the Refuge while near other sentient beings, \
+ and you can be removed from it upon contact with antimagical artifacts."
+ background_icon_state = "bg_heretic"
+ overlay_icon_state = "bg_heretic_border"
+ button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
+ button_icon_state = "ninja_cloak"
+ sound = 'sound/effects/curse2.ogg'
+
+ school = SCHOOL_FORBIDDEN
+ cooldown_time = 1 MINUTES
+
+ invocation_type = INVOCATION_NONE
+ spell_requirements = NONE
+
+/datum/action/cooldown/spell/caretaker/Remove(mob/living/remove_from)
+ if(remove_from.has_status_effect(/datum/status_effect/caretaker_refuge))
+ remove_from.remove_status_effect(/datum/status_effect/caretaker_refuge)
+ return ..()
+
+/datum/action/cooldown/spell/caretaker/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
+
+/datum/action/cooldown/spell/caretaker/cast(atom/cast_on)
+ . = ..()
+ for(var/mob/living/alive in orange(5, owner))
+ if(alive.stat != DEAD && alive.client)
+ owner.balloon_alert(owner, "other minds nearby!")
+ return FALSE
+
+ var/mob/living/carbon/carbon_user = owner
+ if(carbon_user.has_status_effect(/datum/status_effect/caretaker_refuge))
+ carbon_user.remove_status_effect(/datum/status_effect/caretaker_refuge)
+ else
+ carbon_user.apply_status_effect(/datum/status_effect/caretaker_refuge)
+ return TRUE
diff --git a/code/modules/antagonists/heretic/magic/wave_of_desperation.dm b/code/modules/antagonists/heretic/magic/wave_of_desperation.dm
new file mode 100644
index 00000000000..3b78b56ddc0
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/wave_of_desperation.dm
@@ -0,0 +1,79 @@
+/datum/action/cooldown/spell/aoe/wave_of_desperation
+ name = "Wave Of Desperation"
+ desc = "Removes your restraints, repels and knocks down adjacent people, and applies certain effects of the Mansus Grasp upon everything nearby. \
+ Cannot be cast unless you are restrained, and the stress renders you unconscious 12 seconds later!"
+ background_icon_state = "bg_heretic"
+ overlay_icon_state = "bg_heretic_border"
+ button_icon = 'icons/mob/actions/actions_ecult.dmi'
+ button_icon_state = "uncuff"
+ sound = 'sound/magic/swap.ogg'
+
+ school = SCHOOL_FORBIDDEN
+ cooldown_time = 5 MINUTES
+
+ invocation = "F'K 'FF."
+ invocation_type = INVOCATION_WHISPER
+ spell_requirements = NONE
+
+ aoe_radius = 3
+
+/datum/action/cooldown/spell/aoe/wave_of_desperation/is_valid_target(mob/living/carbon/cast_on)
+ return ..() && istype(cast_on) && (cast_on.handcuffed || cast_on.legcuffed)
+
+// Before the cast, we do some small AOE damage around the caster
+/datum/action/cooldown/spell/aoe/wave_of_desperation/before_cast(mob/living/carbon/cast_on)
+ . = ..()
+ if(. & SPELL_CANCEL_CAST)
+ return
+
+ if(cast_on.handcuffed)
+ cast_on.visible_message(span_danger("[cast_on.handcuffed] on [cast_on] shatter!"))
+ QDEL_NULL(cast_on.handcuffed)
+ if(cast_on.legcuffed)
+ cast_on.visible_message(span_danger("[cast_on.legcuffed] on [cast_on] shatters!"))
+ QDEL_NULL(cast_on.legcuffed)
+
+ cast_on.apply_status_effect(/datum/status_effect/heretic_lastresort)
+ new /obj/effect/temp_visual/knockblast(get_turf(cast_on))
+
+ for(var/mob/living/victim in get_things_to_cast_on(cast_on, radius_override = 1))
+ victim.AdjustKnockdown(3 SECONDS)
+ victim.AdjustParalyzed(0.5 SECONDS)
+
+/datum/action/cooldown/spell/aoe/wave_of_desperation/get_things_to_cast_on(atom/center, radius_override)
+ . = list()
+ for(var/atom/nearby in orange(center, radius_override ? radius_override : aoe_radius))
+ if(nearby == owner || nearby == center || isarea(nearby))
+ continue
+ if(!ismob(nearby))
+ . += nearby
+ continue
+ var/mob/living/nearby_mob = nearby
+ if(!isturf(nearby_mob.loc))
+ continue
+ if(IS_HERETIC_OR_MONSTER(nearby_mob))
+ continue
+ if(nearby_mob.can_block_magic(antimagic_flags))
+ continue
+
+ . += nearby_mob
+
+/datum/action/cooldown/spell/aoe/wave_of_desperation/cast_on_thing_in_aoe(atom/victim, atom/caster)
+ if(!ismob(victim))
+ SEND_SIGNAL(owner, COMSIG_HERETIC_MANSUS_GRASP_ATTACK_SECONDARY, victim)
+
+ var/atom/movable/mover = victim
+ if(!istype(mover))
+ return
+
+ if(mover.anchored)
+ return
+ var/our_turf = get_turf(caster)
+ var/throwtarget = get_edge_target_turf(our_turf, get_dir(our_turf, get_step_away(mover, our_turf)))
+ mover.safe_throw_at(throwtarget, 3, 1, force = MOVE_FORCE_STRONG)
+
+/obj/effect/temp_visual/knockblast
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "shield-flash"
+ alpha = 180
+ duration = 1 SECONDS
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index 0f6809a898a..f1c96c51ad4 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -236,3 +236,53 @@
return
addtimer(CALLBACK(src, PROC_REF(create_blade)), blade_recharge_time)
+
+/datum/status_effect/caretaker_refuge
+ id = "Caretaker’s Last Refuge"
+ status_type = STATUS_EFFECT_REFRESH
+ duration = -1
+ alert_type = null
+ var/static/list/caretaking_traits = list(TRAIT_HANDS_BLOCKED, TRAIT_IGNORESLOWDOWN)
+
+/datum/status_effect/caretaker_refuge/on_apply()
+ owner.add_traits(caretaking_traits, TRAIT_STATUS_EFFECT(id))
+ owner.status_flags |= GODMODE
+ animate(owner, alpha = 45,time = 0.5 SECONDS)
+ owner.density = FALSE
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING), PROC_REF(on_focus_lost))
+ RegisterSignal(owner, COMSIG_MOB_BEFORE_SPELL_CAST, PROC_REF(prevent_spell_usage))
+ RegisterSignal(owner, COMSIG_ATOM_HOLYATTACK, PROC_REF(nullrod_handler))
+ return TRUE
+
+/datum/status_effect/caretaker_refuge/on_remove()
+ owner.remove_traits(caretaking_traits, TRAIT_STATUS_EFFECT(id))
+ owner.status_flags &= ~GODMODE
+ owner.alpha = initial(owner.alpha)
+ owner.density = initial(owner.density)
+ UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING))
+ UnregisterSignal(owner, COMSIG_MOB_BEFORE_SPELL_CAST)
+ UnregisterSignal(owner, COMSIG_ATOM_HOLYATTACK)
+ owner.visible_message(
+ span_warning("The haze around [owner] disappears, leaving them materialized!"),
+ span_notice("You exit the refuge."),
+ )
+
+/datum/status_effect/caretaker_refuge/get_examine_text()
+ return span_warning("[owner.p_Theyre()] enveloped in an unholy haze!")
+
+/datum/status_effect/caretaker_refuge/proc/nullrod_handler(datum/source, obj/item/weapon)
+ SIGNAL_HANDLER
+ playsound(get_turf(owner), 'sound/effects/curse1.ogg', 80, TRUE)
+ owner.visible_message(span_warning("[weapon] repels the haze around [owner]!"))
+ owner.remove_status_effect(type)
+
+/datum/status_effect/caretaker_refuge/proc/on_focus_lost()
+ SIGNAL_HANDLER
+ to_chat(owner, span_danger("Without a focus, your refuge weakens and dissipates!"))
+ owner.remove_status_effect(type)
+
+/datum/status_effect/caretaker_refuge/proc/prevent_spell_usage(datum/source, datum/spell)
+ SIGNAL_HANDLER
+ if(!istype(spell, /datum/action/cooldown/spell/caretaker))
+ owner.balloon_alert(owner, "may not cast spells in refuge!")
+ return SPELL_CANCEL_CAST
diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm
index 55c5af3aa08..c286c7b5092 100644
--- a/code/modules/antagonists/heretic/status_effects/debuffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm
@@ -192,3 +192,25 @@
/datum/status_effect/star_mark/extended
duration = 3 MINUTES
+
+// Last Resort
+/datum/status_effect/heretic_lastresort
+ id = "heretic_lastresort"
+ alert_type = /atom/movable/screen/alert/status_effect/heretic_lastresort
+ duration = 12 SECONDS
+ status_type = STATUS_EFFECT_REPLACE
+ tick_interval = -1
+
+/atom/movable/screen/alert/status_effect/heretic_lastresort
+ name = "Last Resort"
+ desc = "Your head spins, heart pumping as fast as it can, losing the fight with the ground. Run to safety!"
+ icon_state = "lastresort"
+
+/datum/status_effect/heretic_lastresort/on_apply()
+ ADD_TRAIT(owner, TRAIT_IGNORESLOWDOWN, TRAIT_STATUS_EFFECT(id))
+ to_chat(owner, span_userdanger("You are on the brink of losing consciousness, run!"))
+ return TRUE
+
+/datum/status_effect/heretic_lastresort/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_IGNORESLOWDOWN, TRAIT_STATUS_EFFECT(id))
+ owner.AdjustUnconscious(20 SECONDS, ignore_canstun = TRUE)
diff --git a/code/modules/antagonists/heretic/status_effects/mark_effects.dm b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
index d5f759ec962..068570f76c7 100644
--- a/code/modules/antagonists/heretic/status_effects/mark_effects.dm
+++ b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
@@ -246,3 +246,17 @@
new teleport_effect(get_turf(owner))
owner.Paralyze(2 SECONDS)
return ..()
+
+// MARK OF KNOCK
+
+/datum/status_effect/eldritch/knock
+ effect_icon_state = "emark7"
+ duration = 10 SECONDS
+
+/datum/status_effect/eldritch/knock/on_apply()
+ . = ..()
+ ADD_TRAIT(owner, TRAIT_ALWAYS_NO_ACCESS, STATUS_EFFECT_TRAIT)
+
+/datum/status_effect/eldritch/knock/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_ALWAYS_NO_ACCESS, STATUS_EFFECT_TRAIT)
+ return ..()
diff --git a/code/modules/antagonists/heretic/structures/knock_final.dm b/code/modules/antagonists/heretic/structures/knock_final.dm
new file mode 100644
index 00000000000..85face85609
--- /dev/null
+++ b/code/modules/antagonists/heretic/structures/knock_final.dm
@@ -0,0 +1,67 @@
+/obj/structure/knock_tear
+ name = "???"
+ desc = "It stares back. Theres no reason to remain. Run."
+ max_integrity = INFINITE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ icon = 'icons/obj/anomaly.dmi'
+ icon_state = "bhole3"
+ color = "#53277E"
+ light_color = "#53277E" //cooler purple
+ light_range = 20
+ anchored = TRUE
+ density = FALSE
+ layer = HIGH_PIPE_LAYER //0.01 above sigil layer used by heretic runes
+ move_resist = INFINITY
+ var/datum/mind/ascendee
+ ///a static list of heretic summons, this shouldnt even matter enough to be static but whatever
+ var/static/list/monster_types
+
+/obj/structure/knock_tear/Initialize(mapload, ascendant)
+ . = ..()
+ transform *= 3
+ if(!monster_types)
+ monster_types = subtypesof(/mob/living/simple_animal/hostile/heretic_summon) - /mob/living/simple_animal/hostile/heretic_summon/armsy/prime
+ if(ascendant)
+ ascendee = ascendant
+ SSpoints_of_interest.make_point_of_interest(src)
+ INVOKE_ASYNC(src, PROC_REF(poll_ghosts))
+
+/obj/structure/knock_tear/proc/poll_ghosts()
+ var/list/candidates = poll_ghost_candidates("Would you like to be a random eldritch monster attacking the crew?", ROLE_SENTIENCE, ROLE_SENTIENCE, 10 SECONDS, POLL_IGNORE_HERETIC_MONSTER)
+ while(LAZYLEN(candidates))
+ var/mob/dead/observer/candidate = pick_n_take(candidates)
+ ghost_to_monster(candidate, should_ask = FALSE)
+
+/obj/structure/knock_tear/attack_ghost(mob/user)
+ . = ..()
+ if(.)
+ return
+ ghost_to_monster(user)
+
+/obj/structure/knock_tear/proc/ghost_to_monster(mob/dead/observer/user, should_ask = TRUE)
+ if(should_ask)
+ var/ask = tgui_alert(user, "Become a monster?", "Ascended Rift", list("Yes", "No"))
+ if(ask != "Yes" || QDELETED(src) || QDELETED(user))
+ return FALSE
+ var/monster_type = pick(monster_types)
+ var/mob/living/monster = new monster_type(loc)
+ monster.key = user.key
+ monster.set_name()
+ var/datum/antagonist/heretic_monster/woohoo_free_antag = new(src)
+ monster.mind.add_antag_datum(woohoo_free_antag)
+ if(ascendee)
+ monster.faction = ascendee.current.faction
+ woohoo_free_antag.set_owner(ascendee)
+ var/datum/objective/kill_all_your_friends = new()
+ kill_all_your_friends.owner = monster.mind
+ kill_all_your_friends.explanation_text = "The station's crew must be culled."
+ kill_all_your_friends.completed = TRUE
+ woohoo_free_antag.objectives += kill_all_your_friends
+
+/obj/structure/knock_tear/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
+ return FALSE
+
+/obj/structure/knock_tear/Destroy(force) //this shouldnt happen but hey
+ if(ascendee)
+ ascendee = null
+ return ..()
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index 2a52705a132..7502165f811 100644
--- a/code/modules/jobs/access.dm
+++ b/code/modules/jobs/access.dm
@@ -6,6 +6,8 @@
return TRUE
if(result_bitflags & COMPONENT_OBJ_DISALLOW) // override all other checks
return FALSE
+ if(HAS_TRAIT(accessor, TRAIT_ALWAYS_NO_ACCESS))
+ return FALSE
//check if it doesn't require any access at all
if(check_access(null))
return TRUE
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 9608db4d4c6..9354f02e2b2 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -88,7 +88,7 @@
//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/proc/bleed(amt)
- if(!blood_volume)
+ if(!blood_volume || (status_flags & GODMODE))
return
blood_volume = max(blood_volume - amt, 0)
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index bad30238246..29e59dc6c71 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/effects/eldritch.dmi b/icons/effects/eldritch.dmi
index 40d4ea80df2..8b7738f3b46 100644
Binary files a/icons/effects/eldritch.dmi and b/icons/effects/eldritch.dmi differ
diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi
index 944337b18e5..a1fc01434e4 100644
Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ
diff --git a/icons/mob/actions/actions_ecult.dmi b/icons/mob/actions/actions_ecult.dmi
index a684dd1fbe6..747b57949be 100644
Binary files a/icons/mob/actions/actions_ecult.dmi and b/icons/mob/actions/actions_ecult.dmi differ
diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi
index 26c177f7293..8a6bab69776 100644
Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ
diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi
index 79c30f5cb19..251458590bd 100644
Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ
diff --git a/icons/obj/service/library.dmi b/icons/obj/service/library.dmi
index dfd8aca6b89..8229c1fc394 100644
Binary files a/icons/obj/service/library.dmi and b/icons/obj/service/library.dmi differ
diff --git a/icons/obj/weapons/khopesh.dmi b/icons/obj/weapons/khopesh.dmi
index 4bbd4d5f0f9..ab7a0c252cb 100644
Binary files a/icons/obj/weapons/khopesh.dmi and b/icons/obj/weapons/khopesh.dmi differ
diff --git a/icons/ui_icons/achievements/achievements.dmi b/icons/ui_icons/achievements/achievements.dmi
index ad34dfd16f0..b3e64c9d09f 100644
Binary files a/icons/ui_icons/achievements/achievements.dmi and b/icons/ui_icons/achievements/achievements.dmi differ
diff --git a/sound/magic/hereticknock.ogg b/sound/magic/hereticknock.ogg
new file mode 100644
index 00000000000..87ca57302a2
Binary files /dev/null and b/sound/magic/hereticknock.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 6c75e06e53c..1c463faa4c4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2854,17 +2854,21 @@
#include "code\modules\antagonists\heretic\items\heretic_blades.dm"
#include "code\modules\antagonists\heretic\items\heretic_necks.dm"
#include "code\modules\antagonists\heretic\items\hunter_rifle.dm"
+#include "code\modules\antagonists\heretic\items\keyring.dm"
+#include "code\modules\antagonists\heretic\items\lintel.dm"
#include "code\modules\antagonists\heretic\items\madness_mask.dm"
#include "code\modules\antagonists\heretic\knowledge\ash_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\blade_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\cosmic_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\flesh_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\general_side.dm"
+#include "code\modules\antagonists\heretic\knowledge\knock_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\rust_lore.dm"
#include "code\modules\antagonists\heretic\knowledge\side_ash_flesh.dm"
#include "code\modules\antagonists\heretic\knowledge\side_blade_rust.dm"
#include "code\modules\antagonists\heretic\knowledge\side_cosmos_ash.dm"
#include "code\modules\antagonists\heretic\knowledge\side_flesh_void.dm"
+#include "code\modules\antagonists\heretic\knowledge\side_knock_flesh.dm"
#include "code\modules\antagonists\heretic\knowledge\side_rust_cosmos.dm"
#include "code\modules\antagonists\heretic\knowledge\side_void_blade.dm"
#include "code\modules\antagonists\heretic\knowledge\starting_lore.dm"
@@ -2874,10 +2878,14 @@
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_map.dm"
#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_moodlets.dm"
#include "code\modules\antagonists\heretic\magic\aggressive_spread.dm"
+#include "code\modules\antagonists\heretic\magic\apetravulnera.dm"
+#include "code\modules\antagonists\heretic\magic\ascended_shapeshift.dm"
#include "code\modules\antagonists\heretic\magic\ash_ascension.dm"
#include "code\modules\antagonists\heretic\magic\ash_jaunt.dm"
#include "code\modules\antagonists\heretic\magic\blood_cleave.dm"
#include "code\modules\antagonists\heretic\magic\blood_siphon.dm"
+#include "code\modules\antagonists\heretic\magic\burglar_finesse.dm"
+#include "code\modules\antagonists\heretic\magic\caretaker.dm"
#include "code\modules\antagonists\heretic\magic\cosmic_expansion.dm"
#include "code\modules\antagonists\heretic\magic\cosmic_runes.dm"
#include "code\modules\antagonists\heretic\magic\eldritch_blind.dm"
@@ -2905,12 +2913,14 @@
#include "code\modules\antagonists\heretic\magic\void_cold_cone.dm"
#include "code\modules\antagonists\heretic\magic\void_phase.dm"
#include "code\modules\antagonists\heretic\magic\void_pull.dm"
+#include "code\modules\antagonists\heretic\magic\wave_of_desperation.dm"
#include "code\modules\antagonists\heretic\mobs\maid_in_mirror.dm"
#include "code\modules\antagonists\heretic\status_effects\buffs.dm"
#include "code\modules\antagonists\heretic\status_effects\debuffs.dm"
#include "code\modules\antagonists\heretic\status_effects\ghoul.dm"
#include "code\modules\antagonists\heretic\status_effects\mark_effects.dm"
#include "code\modules\antagonists\heretic\structures\carving_knife.dm"
+#include "code\modules\antagonists\heretic\structures\knock_final.dm"
#include "code\modules\antagonists\heretic\structures\mawed_crucible.dm"
#include "code\modules\antagonists\highlander\highlander.dm"
#include "code\modules\antagonists\hypnotized\hypnotized.dm"