diff --git a/_maps/templates/heretic_sacrifice_template.dmm b/_maps/templates/heretic_sacrifice_template.dmm
index 4c1c9d30a984..d654f5f92779 100644
--- a/_maps/templates/heretic_sacrifice_template.dmm
+++ b/_maps/templates/heretic_sacrifice_template.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 c62272232938..72fc999fe333 100644
--- a/code/__DEFINES/achievements.dm
+++ b/code/__DEFINES/achievements.dm
@@ -42,6 +42,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/actions.dm b/code/__DEFINES/actions.dm
index 6e57c4457232..836f41a8b638 100644
--- a/code/__DEFINES/actions.dm
+++ b/code/__DEFINES/actions.dm
@@ -8,6 +8,8 @@
#define AB_CHECK_CONSCIOUS (1<<3)
///Action button checks if user is incapacitated
#define AB_CHECK_INCAPACITATED (1<<4)
+///Action button checks if user is jaunting
+#define AB_CHECK_PHASED (1<<5)
DEFINE_BITFIELD(check_flags, list(
"CHECK IF HANDS BLOCKED" = AB_CHECK_HANDS_BLOCKED,
@@ -15,6 +17,7 @@ DEFINE_BITFIELD(check_flags, list(
"CHECK IF LYING DOWN" = AB_CHECK_LYING,
"CHECK IF CONSCIOUS" = AB_CHECK_CONSCIOUS,
"CHECK IF INCAPACITATED" = AB_CHECK_INCAPACITATED,
+ "CHECK IF TEMPORARILY INCORPOREAL" = AB_CHECK_PHASED,
))
///Action button triggered with right click
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 03af4715af11..14641092d6dc 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/colors.dm b/code/__DEFINES/colors.dm
index deb84d0f0b0f..6157110d4c0e 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -93,6 +93,7 @@
#define COLOR_STRONG_MAGENTA "#B800B8"
#define COLOR_PURPLE "#800080"
#define COLOR_VIOLET "#B900F7"
+#define COLOR_VOID_PURPLE "#53277E"
#define COLOR_STRONG_VIOLET "#6927C5"
#define COLOR_DARK_PURPLE "#551A8B"
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 d47833f84c0d..a2198cfb63c1 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/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
index 42934c3c7fa9..d2b720057c4f 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -81,6 +81,7 @@
#define COMSIG_CARBON_EMBED_REMOVAL "item_embed_remove_safe"
///Called when someone attempts to cuff a carbon
#define COMSIG_CARBON_CUFF_ATTEMPTED "carbon_attempt_cuff"
+ #define COMSIG_CARBON_CUFF_PREVENT (1<<0)
///Called when a carbon mutates (source = dna, mutation = mutation added)
#define COMSIG_CARBON_GAIN_MUTATION "carbon_gain_mutation"
///Called when a carbon loses a mutation (source = dna, mutation = mutation lose)
diff --git a/code/__DEFINES/magic.dm b/code/__DEFINES/magic.dm
index 34ec4b6659b6..ecc470c04e90 100644
--- a/code/__DEFINES/magic.dm
+++ b/code/__DEFINES/magic.dm
@@ -50,8 +50,7 @@
/// Whether the spell can be cast by mobs who are brains / mmis.
/// When applying, bear in mind most spells will not function for brains out of the box.
#define SPELL_CASTABLE_AS_BRAIN (1 << 2)
-/// Whether the spell can be cast while phased, such as blood crawling, ethereal jaunting or using rod form.
-#define SPELL_CASTABLE_WHILE_PHASED (1 << 3)
+
/// Whether the spell can be cast while the user has antimagic on them that corresponds to the spell's own antimagic flags.
#define SPELL_REQUIRES_NO_ANTIMAGIC (1 << 4)
/// Whether the spell requires being on the station z-level to be cast.
@@ -66,7 +65,6 @@
DEFINE_BITFIELD(spell_requirements, list(
"SPELL_CASTABLE_AS_BRAIN" = SPELL_CASTABLE_AS_BRAIN,
- "SPELL_CASTABLE_WHILE_PHASED" = SPELL_CASTABLE_WHILE_PHASED,
"SPELL_CASTABLE_WITHOUT_INVOCATION" = SPELL_CASTABLE_WITHOUT_INVOCATION,
"SPELL_REQUIRES_HUMAN" = SPELL_REQUIRES_HUMAN,
"SPELL_REQUIRES_MIME_VOW" = SPELL_REQUIRES_MIME_VOW,
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 0697a0d709c8..caa722e859ef 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -1138,6 +1138,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/__HELPERS/areas.dm b/code/__HELPERS/areas.dm
index d1bad98a1c6a..01e5f911750d 100644
--- a/code/__HELPERS/areas.dm
+++ b/code/__HELPERS/areas.dm
@@ -10,7 +10,7 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/station/en
// The dirs are connected turfs in the same space
// break_if_found is a typecache of turf/area types to return false if found
// Please keep this proc type agnostic. If you need to restrict it do it elsewhere or add an arg.
-/proc/detect_room(turf/origin, list/break_if_found, max_size=INFINITY)
+/proc/detect_room(turf/origin, list/break_if_found = list(), max_size=INFINITY)
if(origin.blocks_air)
return list(origin)
diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm
index 9819b3499a42..132beed9b331 100644
--- a/code/_globalvars/phobias.dm
+++ b/code/_globalvars/phobias.dm
@@ -13,6 +13,7 @@ GLOBAL_LIST_INIT(phobia_types, sort_list(list(
"falling",
"greytide",
"guns",
+ "heresy",
"insects",
"lizards",
"robots",
@@ -36,6 +37,7 @@ GLOBAL_LIST_INIT(phobia_regexes, list(
"falling" = construct_phobia_regex("falling"),
"greytide" = construct_phobia_regex("greytide"),
"guns" = construct_phobia_regex("guns"),
+ "heresy" = construct_phobia_regex("heresy"),
"insects" = construct_phobia_regex("insects"),
"lizards" = construct_phobia_regex("lizards"),
"ocky icky" = construct_phobia_regex("ocky icky"),
@@ -296,18 +298,90 @@ GLOBAL_LIST_INIT(phobia_objs, list(
/obj/machinery/stasis,
/obj/structure/sign/departments/medbay,
)),
-
- "authority" = typecacheof(list(
- /obj/item/card/id/advanced/centcom,
- /obj/item/card/id/advanced/gold,
- /obj/item/card/id/advanced/silver,
- /obj/item/clothing/under/rank/captain,
- /obj/item/clothing/under/rank/centcom/commander,
- /obj/item/clothing/under/rank/centcom/officer,
- /obj/item/clothing/under/rank/civilian/head_of_personnel,
- /obj/item/clothing/under/rank/engineering/chief_engineer,
- /obj/item/clothing/under/rank/medical/chief_medical_officer,
- /obj/item/clothing/under/rank/rnd/research_director,
+ "greytide" = (typecacheof(list(
+ /obj/item/clothing/under/color/grey,
+ /obj/item/melee/baton/security/cattleprod,
+ /obj/item/spear,
+ /obj/item/toy/figure/assistant,
+ /obj/structure/statue/sandstone/assistant,
+ )) + typecacheof(list(/obj/item/clothing/mask/gas), ignore_root_path = FALSE, only_root_path = TRUE // to match only specific items in this phobia and not subtypes, use an additional typecacheof w/ ignore_root_path set FALSE and only_root_patch set TRUE
+ )),
+ "guns" = typecacheof(list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/gun/ballistic,
+ /obj/item/gun/blastcannon,
+ /obj/item/gun/chem,
+ /obj/item/gun/energy,
+ /obj/item/gun/grenadelauncher,
+ /obj/item/gun/syringe,
+ /obj/item/mecha_ammo,
+ /obj/item/mecha_parts/mecha_equipment/weapon/ballistic,
+ /obj/item/mecha_parts/mecha_equipment/weapon/energy,
+ /obj/item/storage/belt/bandolier,
+ /obj/item/storage/belt/holster,
+ /obj/machinery/porta_turret,
+ /obj/machinery/power/emitter,
+ )),
+ "heresy" = typecacheof(list(
+ /obj/effect/floating_blade,
+ /obj/effect/forcefield/cosmic_field,
+ /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/a762/lionhunter,
+ /obj/item/ammo_casing/a762/lionhunter,
+ /obj/item/clothing/mask/madness_mask,
+ /obj/item/clothing/neck/eldritch_amulet,
+ /obj/item/clothing/neck/fake_heretic_amulet,
+ /obj/item/clothing/neck/heretic_focus,
+ /obj/item/clothing/suit/hooded/cultrobes/eldritch,
+ /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,
+ /obj/item/reagent_containers/cup/beaker/eldritch,
+ /obj/item/toy/eldritch_book,
+ /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,
+ /obj/item/clothing/suit/hooded/bee_costume,
+ /obj/item/toy/plush/beeplushie,
+ /obj/item/toy/plush/moth,
+ /obj/structure/beebox,
+ )),
+ "lizards" = typecacheof(list(
+ /obj/item/clothing/head/costume/lizard,
+ /obj/item/clothing/shoes/cowboy/lizard,
+ /obj/item/food/kebab/tail,
+ /obj/item/organ/external/tail/lizard,
+ /obj/item/reagent_containers/cup/glass/bottle/lizardwine,
+ /obj/item/toy/plush/lizard_plushie,
+ )),
+ "robots" = typecacheof(list(
+ /obj/item/ai_module,
+ /obj/item/aicard,
+ /obj/item/toy/figure/borg,
+ /obj/item/toy/talking/ai,
+ /obj/machinery/computer/upload,
+ /obj/machinery/recharge_station,
+ /obj/structure/statue/diamond/ai1,
+ /obj/structure/statue/diamond/ai2,
+ /obj/structure/statue/silver/medborg,
+ /obj/structure/statue/silver/secborg,
+ )),
+ "security" = typecacheof(list(
+ /obj/effect/client_image_holder/securitron,
+ /obj/item/clothing/under/rank/security/detective,
/obj/item/clothing/under/rank/security/head_of_security,
/obj/item/clothing/under/rank/cargo/qm,
/obj/item/clothing/neck/cloak/qm,
diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm
index 483bdf906288..a1cc77060a1f 100644
--- a/code/datums/achievements/misc_achievements.dm
+++ b/code/datums/achievements/misc_achievements.dm
@@ -141,6 +141,12 @@
database_id = MEDAL_ARCHMAGE
icon = "archmage"
+/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/toolbox_soul
name = "SOUL'd Out"
desc = "My eternal soul was destroyed to make a toolbox look funny and all I got was this achievement..."
diff --git a/code/datums/actions/action.dm b/code/datums/actions/action.dm
index da89d73bb79a..7a981d173bfd 100644
--- a/code/datums/actions/action.dm
+++ b/code/datums/actions/action.dm
@@ -96,13 +96,15 @@
if(check_flags & AB_CHECK_CONSCIOUS)
RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(update_status_on_signal))
if(check_flags & AB_CHECK_INCAPACITATED)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED), PROC_REF(update_status_on_signal))
+ RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED), SIGNAL_REMOVETRAIT(TRAIT_INCAPACITATED)), PROC_REF(update_status_on_signal))
if(check_flags & AB_CHECK_IMMOBILE)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED), PROC_REF(update_status_on_signal))
+ RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED), SIGNAL_REMOVETRAIT(TRAIT_IMMOBILIZED)), PROC_REF(update_status_on_signal))
if(check_flags & AB_CHECK_HANDS_BLOCKED)
- RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED), PROC_REF(update_status_on_signal))
+ RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED), SIGNAL_REMOVETRAIT(TRAIT_HANDS_BLOCKED)), PROC_REF(update_status_on_signal))
if(check_flags & AB_CHECK_LYING)
RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(update_status_on_signal))
+ if(check_flags & AB_CHECK_PHASED)
+ RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_MAGICALLY_PHASED), SIGNAL_REMOVETRAIT(TRAIT_MAGICALLY_PHASED)), PROC_REF(update_status_on_signal))
if(owner_has_control)
GiveAction(grant_to)
@@ -130,6 +132,11 @@
SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED),
SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED),
SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED),
+ SIGNAL_ADDTRAIT(TRAIT_MAGICALLY_PHASED),
+ SIGNAL_REMOVETRAIT(TRAIT_HANDS_BLOCKED),
+ SIGNAL_REMOVETRAIT(TRAIT_IMMOBILIZED),
+ SIGNAL_REMOVETRAIT(TRAIT_INCAPACITATED),
+ SIGNAL_REMOVETRAIT(TRAIT_MAGICALLY_PHASED),
))
if(target == owner)
@@ -174,6 +181,10 @@
if (feedback)
owner.balloon_alert(owner, "unconscious!")
return FALSE
+ if((check_flags & AB_CHECK_PHASED) && HAS_TRAIT(owner, TRAIT_MAGICALLY_PHASED))
+ if (feedback)
+ owner.balloon_alert(owner, "incorporeal!")
+ return FALSE
return TRUE
/// Builds / updates all buttons we have shared or given out
diff --git a/code/datums/actions/mobs/open_mob_commands.dm b/code/datums/actions/mobs/open_mob_commands.dm
index 49a130a87309..e7ffd104effb 100644
--- a/code/datums/actions/mobs/open_mob_commands.dm
+++ b/code/datums/actions/mobs/open_mob_commands.dm
@@ -5,10 +5,11 @@
overlay_icon_state = "bg_heretic_border"
button_icon = 'icons/mob/actions/actions_ecult.dmi'
button_icon_state = "stargazer_menu"
+ check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED | AB_CHECK_PHASED
/// Weakref for storing our stargazer
var/datum/weakref/our_mob
-/datum/action/cooldown/open_mob_commands/Grant(mob/granted_to, mob/living/basic/star_gazer/our_mob_input)
+/datum/action/cooldown/open_mob_commands/Grant(mob/granted_to, mob/living/basic/heretic_summon/star_gazer/our_mob_input)
. = ..()
our_mob = WEAKREF(our_mob_input)
@@ -18,7 +19,7 @@
/// Opens the pet command options menu for a mob.
/datum/action/cooldown/open_mob_commands/proc/open_menu()
- var/mob/living/basic/star_gazer/our_mob_resolved = our_mob?.resolve()
+ var/mob/living/basic/heretic_summon/star_gazer/our_mob_resolved = our_mob?.resolve()
if(our_mob_resolved)
var/datum/component/obeys_commands/command_component = our_mob_resolved.GetComponent(/datum/component/obeys_commands)
if(command_component)
diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm
index 3168b360b241..51cb2732e583 100644
--- a/code/datums/brain_damage/phobia.dm
+++ b/code/datums/brain_damage/phobia.dm
@@ -198,6 +198,14 @@
phobia_type = "strangers"
random_gain = FALSE
+/datum/brain_trauma/mild/phobia/heresy
+ phobia_type = "heresy"
+ random_gain = FALSE
+
+/datum/brain_trauma/mild/phobia/insects
+ phobia_type = "insects"
+ random_gain = FALSE
+
/datum/brain_trauma/mild/phobia/birds
phobia_type = "birds"
random_gain = FALSE
diff --git a/code/datums/components/anti_magic.dm b/code/datums/components/anti_magic.dm
index 47cc5fa24cc0..35f250061988 100644
--- a/code/datums/components/anti_magic.dm
+++ b/code/datums/components/anti_magic.dm
@@ -42,6 +42,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_OBJ), PROC_REF(on_attack))
else if(ismob(parent))
RegisterSignal(parent, COMSIG_MOB_RECEIVE_MAGIC, PROC_REF(block_receiving_magic), override = TRUE)
RegisterSignal(parent, COMSIG_MOB_RESTRICT_MAGIC, PROC_REF(restrict_casting_magic), override = TRUE)
@@ -158,3 +159,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/components/death_linked.dm b/code/datums/components/death_linked.dm
new file mode 100644
index 000000000000..59d2ce5e855b
--- /dev/null
+++ b/code/datums/components/death_linked.dm
@@ -0,0 +1,30 @@
+/**
+ * ## Death link component
+ *
+ * When the owner of this component dies it also gibs a linked mob
+ */
+/datum/component/death_linked
+ ///The mob that also dies when the user dies
+ var/datum/weakref/linked_mob
+
+/datum/component/death_linked/Initialize(mob/living/target_mob)
+ . = ..()
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+ if(isnull(target_mob))
+ stack_trace("[type] added to [parent] with no linked mob.")
+ src.linked_mob = WEAKREF(target_mob)
+
+/datum/component/death_linked/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_death))
+
+/datum/component/death_linked/UnregisterFromParent()
+ . = ..()
+ UnregisterSignal(parent, COMSIG_LIVING_DEATH)
+
+///signal called by the stat of the target changing
+/datum/component/death_linked/proc/on_death(mob/living/target, gibbed)
+ SIGNAL_HANDLER
+ var/mob/living/linked_mob_resolved = linked_mob?.resolve()
+ linked_mob_resolved?.gib()
diff --git a/code/datums/elements/death_linked.dm b/code/datums/elements/death_linked.dm
deleted file mode 100644
index c5d2c6c422c6..000000000000
--- a/code/datums/elements/death_linked.dm
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * ## death linkage element!
- *
- * Bespoke element that when the owner dies, the linked mob dies too.
- */
-/datum/element/death_linked
- element_flags = ELEMENT_BESPOKE
- argument_hash_start_idx = 3
- ///The mob that also dies when the user dies
- var/datum/weakref/linked_mob
-
-/datum/element/death_linked/Attach(datum/target, mob/living/target_mob)
- . = ..()
- if(!isliving(target))
- return ELEMENT_INCOMPATIBLE
- if(!target_mob)
- stack_trace("[type] added to [target] with NO MOB.")
- src.linked_mob = WEAKREF(target_mob)
- RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(on_death))
-
-/datum/element/death_linked/Detach(datum/target)
- . = ..()
- UnregisterSignal(target, COMSIG_LIVING_DEATH)
-
-///signal called by the stat of the target changing
-/datum/element/death_linked/proc/on_death(mob/living/target, gibbed)
- SIGNAL_HANDLER
- var/mob/living/linked_mob_resolved = linked_mob?.resolve()
- linked_mob_resolved?.death(TRUE)
diff --git a/code/datums/elements/embed.dm b/code/datums/elements/embed.dm
index 854193c19746..df591d50e00c 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/datums/elements/strippable.dm b/code/datums/elements/strippable.dm
index c3f5d72cf701..ce1c0d3899d9 100644
--- a/code/datums/elements/strippable.dm
+++ b/code/datums/elements/strippable.dm
@@ -339,7 +339,7 @@
continue
var/obj/item/item = item_data.get_item(owner)
- if (isnull(item) || (HAS_TRAIT(item, TRAIT_NO_STRIP)))
+ if (isnull(item) || (HAS_TRAIT(item, TRAIT_NO_STRIP) || (item.item_flags & EXAMINE_SKIP)))
items[strippable_key] = result
continue
diff --git a/code/datums/elements/tear_wall.dm b/code/datums/elements/tear_wall.dm
deleted file mode 100644
index 0d24bbda2898..000000000000
--- a/code/datums/elements/tear_wall.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Attached to a basic mob that will then be able to tear down a wall after some time.
- */
-/datum/element/tear_wall
- element_flags = ELEMENT_BESPOKE
- argument_hash_start_idx = 3
- /// The rate at which we can break regular walls
- var/regular_tear_time
- /// The rate at which we can break reinforced walls
- var/reinforced_tear_time
-
-/datum/element/tear_wall/Attach(datum/target, regular_tear_time = 2 SECONDS, reinforced_tear_time = 4 SECONDS)
- . = ..()
- if(!isbasicmob(target))
- return ELEMENT_INCOMPATIBLE
-
- src.regular_tear_time = regular_tear_time
- src.reinforced_tear_time = reinforced_tear_time
- RegisterSignal(target, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(attack_wall))
-
-/datum/element/bonus_damage/Detach(datum/source)
- UnregisterSignal(source, COMSIG_HOSTILE_POST_ATTACKINGTARGET)
- return ..()
-
-/// Checks if we are attacking a wall
-/datum/element/tear_wall/proc/attack_wall(mob/living/basic/attacker, atom/target, success)
- SIGNAL_HANDLER
-
- if(!iswallturf(target))
- return
- var/turf/closed/wall/thewall = target
- var/prying_time = regular_tear_time
- if(istype(thewall, /turf/closed/wall/r_wall))
- prying_time = reinforced_tear_time
- INVOKE_ASYNC(src, PROC_REF(async_attack_wall), attacker, thewall, prying_time)
-
-/// Performs taking down the wall
-/datum/element/tear_wall/proc/async_attack_wall(mob/living/basic/attacker, turf/closed/wall/thewall, prying_time)
- if(DOING_INTERACTION_WITH_TARGET(attacker, thewall))
- attacker.balloon_alert(attacker, "busy!")
- return
- to_chat(attacker, span_warning("You begin tearing through the wall..."))
- playsound(attacker, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
- if(do_after(attacker, prying_time, target = thewall))
- if(isopenturf(thewall))
- return
- thewall.dismantle_wall(1)
- playsound(attacker, 'sound/effects/meteorimpact.ogg', 100, TRUE)
diff --git a/code/datums/elements/wall_tearer.dm b/code/datums/elements/wall_tearer.dm
index 19e84dd2720b..2c9ff5416d59 100644
--- a/code/datums/elements/wall_tearer.dm
+++ b/code/datums/elements/wall_tearer.dm
@@ -20,7 +20,7 @@
/// What interaction key do we use for our interaction
var/do_after_key
-/datum/element/wall_tearer/Attach(datum/target, allow_reinforced = TRUE, tear_time = 4 SECONDS, reinforced_multiplier = 3, do_after_key = null)
+/datum/element/wall_tearer/Attach(datum/target, allow_reinforced = TRUE, tear_time = 2 SECONDS, reinforced_multiplier = 2, do_after_key = null)
. = ..()
if (!isliving(target))
return ELEMENT_INCOMPATIBLE
@@ -60,6 +60,7 @@
var/is_valid = validate_target(target, tearer)
if (is_valid != WALL_TEAR_ALLOWED)
return
+ tearer.do_attack_animation(target)
target.AddComponent(/datum/component/torn_wall)
is_valid = validate_target(target, tearer) // And now we might have just destroyed it
if (is_valid == WALL_TEAR_ALLOWED)
diff --git a/code/datums/memory/general_memories.dm b/code/datums/memory/general_memories.dm
index 1326b72fe166..89e0a4e34ca9 100644
--- a/code/datums/memory/general_memories.dm
+++ b/code/datums/memory/general_memories.dm
@@ -725,14 +725,14 @@
return list("[protagonist_name] [mood_verb] as they [result] the deadly game of roulette.")
/// When a heretic finishes their ritual of knowledge
-/datum/memory/heretic_knowlege_ritual
+/datum/memory/heretic_knowledge_ritual
story_value = STORY_VALUE_AMAZING
// Protagonist = heretic
-/datum/memory/heretic_knowlege_ritual/get_names()
+/datum/memory/heretic_knowledge_ritual/get_names()
return list("[protagonist_name] absorbing boundless knowledge through eldritch research.")
-/datum/memory/heretic_knowlege_ritual/get_starts()
+/datum/memory/heretic_knowledge_ritual/get_starts()
return list(
"[protagonist_name] laying out a circle of green tar and candles",
"multiple books around [protagonist_name] flipping open",
@@ -742,16 +742,16 @@
"a wide, strange looking circle, with [protagonist_name] sketching it"
)
-/datum/memory/heretic_knowlege_ritual/get_moods()
+/datum/memory/heretic_knowledge_ritual/get_moods()
return list("[protagonist_name] [mood_verb] as their hand glows with power.")
-/datum/memory/heretic_knowlege_ritual/get_happy_moods()
+/datum/memory/heretic_knowledge_ritual/get_happy_moods()
return list("cackling madly")
-/datum/memory/heretic_knowlege_ritual/get_neutral_moods()
+/datum/memory/heretic_knowledge_ritual/get_neutral_moods()
return list("staring blankly with a wide grin")
-/datum/memory/heretic_knowlege_ritual/get_sad_moods()
+/datum/memory/heretic_knowledge_ritual/get_sad_moods()
return list("cackling insanely")
/// Failed to defuse a bomb, by triggering it early.
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index a24aeb45ce4b..79d05136d15a 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -64,7 +64,8 @@
if(!istype(C))
return
- SEND_SIGNAL(C, COMSIG_CARBON_CUFF_ATTEMPTED, user)
+ if(SEND_SIGNAL(C, COMSIG_CARBON_CUFF_ATTEMPTED, user) & COMSIG_CARBON_CUFF_PREVENT)
+ return
if(iscarbon(user) && (HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))) //Clumsy people have a 50% chance to handcuff themselves instead of their target.
to_chat(user, span_warning("Uh... how do those things work?!"))
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 29cebd8432f5..21fc18326239 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -74,6 +74,7 @@
/obj/item/weldingtool,
/obj/item/wirecutters,
/obj/item/wrench,
+ /obj/item/melee/sickly_blade/knock,
/obj/item/clockwork/replica_fabricator, //monkestation edit
/obj/item/clockwork/clockwork_slab, //monkestation edit
))
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index 1f751e904896..4a8cad5c2684 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -83,6 +83,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/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index ac26d050ea13..8d083bcafd8f 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -985,8 +985,6 @@ GLOBAL_PROTECT(admin_verbs_poll)
var/reqs = initial(spell.spell_requirements)
if(reqs & SPELL_CASTABLE_AS_BRAIN)
real_reqs += "Castable as brain"
- if(reqs & SPELL_CASTABLE_WHILE_PHASED)
- real_reqs += "Castable phased"
if(reqs & SPELL_REQUIRES_HUMAN)
real_reqs += "Must be human"
if(reqs & SPELL_REQUIRES_MIME_VOW)
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index 84a26b8965f7..3a1559ca926e 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -31,6 +31,8 @@
var/heretic_path = PATH_START
/// A sum of how many knowledge points this heretic CURRENTLY has. Used to research.
var/knowledge_points = 1
+ /// How many side path points the heretic has. He gains one of these per main path that splits into two sidepaths. These can be used in place of knowledge points for side paths only.
+ var/side_path_points = 0
/// The time between gaining influence passively. The heretic gain +1 knowledge points every this duration of time.
var/passive_gain_timer = 20 MINUTES
/// Assoc list of [typepath] = [knowledge instance]. A list of all knowledge this heretic's reserached.
@@ -43,6 +45,8 @@
var/high_value_sacrifices = 0
/// Lazy assoc list of [refs to humans] to [image previews of the human]. Humans that we have as sacrifice targets.
var/list/mob/living/carbon/human/sac_targets
+ /// List of all sacrifice target's names, used for end of round report
+ var/list/all_sac_targets = list()
/// Whether we're drawing a rune or not
var/drawing_rune = FALSE
/// A static typecache of all tools we can scribe with.
@@ -59,6 +63,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,
@@ -68,6 +73,7 @@
PATH_VOID = COLOR_CYAN,
PATH_BLADE = COLOR_SILVER,
PATH_COSMIC = COLOR_PURPLE,
+ PATH_KNOCK = COLOR_YELLOW,
)
/datum/antagonist/heretic/Destroy()
@@ -78,6 +84,7 @@
var/list/data = list()
data["charges"] = knowledge_points
+ data["side_charges"] = side_path_points
data["total_sacrifices"] = total_sacrifices
data["ascended"] = ascended
@@ -91,7 +98,10 @@
knowledge_data["desc"] = initial(knowledge.desc)
knowledge_data["gainFlavor"] = initial(knowledge.gain_text)
knowledge_data["cost"] = initial(knowledge.cost)
- knowledge_data["disabled"] = (initial(knowledge.cost) > knowledge_points)
+ if(initial(knowledge.route) == PATH_SIDE)
+ knowledge_data["disabled"] = (initial(knowledge.cost) > knowledge_points + side_path_points)
+ else
+ knowledge_data["disabled"] = (initial(knowledge.cost) > knowledge_points)
// Final knowledge can't be learned until all objectives are complete.
if(ispath(knowledge, /datum/heretic_knowledge/ultimate))
@@ -134,14 +144,22 @@
if(!ispath(researched_path))
CRASH("Heretic attempted to learn non-heretic_knowledge path! (Got: [researched_path])")
- if(initial(researched_path.cost) > knowledge_points)
- return TRUE
+ // If side path and has path points, buy!
+ var/coupon = FALSE
+ if((initial(researched_path.route) == PATH_SIDE) && side_path_points)
+ coupon = TRUE
+ // else try normal purchase
+ else if(initial(researched_path.cost) > knowledge_points)
+ return
+
if(!gain_knowledge(researched_path))
return TRUE
- log_heretic_knowledge("[key_name(owner)] gained knowledge: [initial(researched_path.name)]")
- add_event_to_buffer(owner, data = "gained knowledge: [initial(researched_path.name)]", log_key = "HERETIC")
- knowledge_points -= initial(researched_path.cost)
+ log_heretic_knowledge("[key_name(owner)] gained knowledge: [initial(researched_path.name)][coupon ? "(via free side-path point)" : ""]")
+ if(coupon)
+ side_path_points -= initial(researched_path.cost)
+ else
+ knowledge_points -= initial(researched_path.cost)
return TRUE
/datum/antagonist/heretic/ui_status(mob/user, datum/ui_state/state)
@@ -285,14 +303,14 @@
* * drawing_time - how long the do_after takes to make the rune
* * additional checks - optional callbacks to be ran while drawing the rune
*/
-/datum/antagonist/heretic/proc/try_draw_rune(mob/living/user, turf/target_turf, drawing_time = 30 SECONDS, additional_checks)
+/datum/antagonist/heretic/proc/try_draw_rune(mob/living/user, turf/target_turf, drawing_time = 20 SECONDS, additional_checks)
for(var/turf/nearby_turf as anything in RANGE_TURFS(1, target_turf))
if(!isopenturf(nearby_turf) || is_type_in_typecache(nearby_turf, blacklisted_rune_turfs))
target_turf.balloon_alert(user, "invalid placement for rune!")
return
if(locate(/obj/effect/heretic_rune) in range(3, target_turf))
- target_turf.balloon_alert(user, "to close to another rune!")
+ target_turf.balloon_alert(user, "too close to another rune!")
return
if(drawing_rune)
@@ -310,16 +328,16 @@
* * drawing_time - how long the do_after takes to make the rune
* * additional checks - optional callbacks to be ran while drawing the rune
*/
-/datum/antagonist/heretic/proc/draw_rune(mob/living/user, turf/target_turf, drawing_time = 30 SECONDS, additional_checks)
+/datum/antagonist/heretic/proc/draw_rune(mob/living/user, turf/target_turf, drawing_time = 20 SECONDS, additional_checks)
drawing_rune = TRUE
var/rune_colour = path_to_rune_color[heretic_path]
target_turf.balloon_alert(user, "drawing rune...")
var/obj/effect/temp_visual/drawing_heretic_rune/drawing_effect
- if (drawing_time >= (30 SECONDS))
- drawing_effect = new(target_turf, rune_colour)
- else
+ if (drawing_time < (10 SECONDS))
drawing_effect = new /obj/effect/temp_visual/drawing_heretic_rune/fast(target_turf, rune_colour)
+ else
+ drawing_effect = new(target_turf, rune_colour)
if(!do_after(user, drawing_time, target_turf, extra_checks = additional_checks))
target_turf.balloon_alert(user, "interrupted!")
@@ -355,16 +373,14 @@
GLOB.reality_smash_track.rework_network()
-/// Signal proc for [COMSIG_LIVING_POST_FULLY_HEAL], when we get fullhealed / ahealed,
-/// all of our organs are "deleted" and regenerated (cause it's a full heal)
-/// which unfortunately means we lose our living heart.
-/// So, we'll give them some lee-way and give them back the living heart afterwards
-/// (Maybe put this behind only admin_revives only? Not sure.)
-/datum/antagonist/heretic/proc/after_fully_healed(mob/living/source, admin_revive)
+/// Signal proc for [COMSIG_LIVING_POST_FULLY_HEAL],
+/// Gives the heretic aliving heart on aheal or organ refresh
+/datum/antagonist/heretic/proc/after_fully_healed(mob/living/source, heal_flags)
SIGNAL_HANDLER
- var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart)
- heart_knowledge.on_research(source)
+ if(heal_flags & (HEAL_REFRESH_ORGANS|HEAL_ADMIN))
+ var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart)
+ heart_knowledge.on_research(source, src)
/// Signal proc for [COMSIG_LIVING_CULT_SACRIFICED] to reward cultists for sacrificing a heretic
/datum/antagonist/heretic/proc/on_cult_sacrificed(mob/living/source, list/invokers)
@@ -411,6 +427,7 @@
LAZYSET(sac_targets, target, target_image)
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_target_deleted))
+ all_sac_targets += target.real_name
/**
* Removes [target] from the heretic's sacrifice list.
@@ -450,7 +467,7 @@
parts += printplayer(owner)
parts += "Sacrifices Made: [total_sacrifices]"
-
+ parts += "The heretic's sacrifice targets were: [english_list(all_sac_targets, nothing_text = "No one")]."
if(length(objectives))
var/count = 1
for(var/datum/objective/objective as anything in objectives)
@@ -493,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.
@@ -568,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()
@@ -733,8 +763,8 @@
target_amount = main_path_length
// Add in the base research we spawn with, otherwise it'd be too easy.
target_amount += length(GLOB.heretic_start_knowledge)
- // And add in some buffer, to require some sidepathing.
- target_amount += rand(2, 4)
+ // And add in some buffer, to require some sidepathing, especially since heretics get some free side paths.
+ target_amount += rand(5, 7)
update_explanation_text()
/datum/objective/heretic_research/update_explanation_text()
@@ -762,17 +792,3 @@
suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
r_hand = /obj/item/melee/touch_attack/mansus_fist
-
-/datum/antagonist/heretic/antag_token(datum/mind/hosts_mind, mob/spender)
- . = ..()
- var/datum/antagonist/heretic/new_heretic = new()
- if(isobserver(spender))
- var/mob/living/carbon/human/newmob = spender.change_mob_type( /mob/living/carbon/human , null, null, TRUE )
- newmob.equipOutfit(/datum/outfit/job/assistant)
- newmob.mind.add_antag_datum(new_heretic)
- else
- hosts_mind.add_antag_datum(new_heretic)
-
- if(!new_heretic.has_living_heart())
- var/datum/heretic_knowledge/living_heart/heart_knowledge = new_heretic.get_knowledge(/datum/heretic_knowledge/living_heart)
- heart_knowledge.on_research(hosts_mind.current)
diff --git a/code/modules/antagonists/heretic/heretic_focus.dm b/code/modules/antagonists/heretic/heretic_focus.dm
index b7c79b6d6caa..45bbf743b8cd 100644
--- a/code/modules/antagonists/heretic/heretic_focus.dm
+++ b/code/modules/antagonists/heretic/heretic_focus.dm
@@ -46,7 +46,7 @@
if(!IS_HERETIC(user))
return
- if(!(source.slot_flags & slot))
+ if(source.slot_flags && !(source.slot_flags & slot))
return
ADD_TRAIT(user, TRAIT_ALLOW_HERETIC_CASTING, ELEMENT_TRAIT(source))
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index 38b14e65b313..ba13087872cc 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -25,11 +25,16 @@
var/list/banned_knowledge = list()
/// Assoc list of [typepaths we need] to [amount needed].
/// If set, this knowledge allows the heretic to do a ritual on a transmutation rune with the components set.
+ /// If one of the items in the list is a list, it's treated as 'any of these items will work'
var/list/required_atoms
/// Paired with above. If set, the resulting spawned atoms upon ritual completion.
var/list/result_atoms = list()
- /// Cost of knowledge in knowlege points
+ /// If set, required_atoms checks for these *exact* types and doesn't allow them to be ingredients.
+ var/list/banned_atom_types = list()
+ /// Cost of knowledge in knowledge points
var/cost = 0
+ /// If true, adds side path points according to value. Only main branch powers that split into sidepaths should have this.
+ var/adds_sidepath_points = 0
/// The priority of the knowledge. Higher priority knowledge appear higher in the ritual list.
/// Number itself is completely arbitrary. Does not need to be set for non-ritual knowledge.
var/priority = 0
@@ -58,6 +63,8 @@
if(gain_text)
to_chat(user, span_warning("[gain_text]"))
+ // Usually zero
+ our_heretic.side_path_points += adds_sidepath_points
on_gain(user, our_heretic)
/**
@@ -112,15 +119,27 @@
/datum/heretic_knowledge/proc/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
return TRUE
+/**
+ * Parses specific items into a more reaadble form.
+ * Can be overriden by knoweldge subtypes.
+ */
+/datum/heretic_knowledge/proc/parse_required_item(atom/item_path, number_of_things)
+ // If we need a human, there is a high likelihood we actually need a (dead) body
+ if(ispath(item_path, /mob/living/carbon/human))
+ return "bod[number_of_things > 1 ? "ies" : "y"]"
+ if(ispath(item_path, /mob/living))
+ return "carcass[number_of_things > 1 ? "es" : ""] of any kind"
+ return "[initial(item_path.name)]\s"
+
/**
* Called whenever the knowledge's associated ritual is completed successfully.
*
* Creates atoms from types in result_atoms.
- * Override this is you want something else to happen.
+ * Override this if you want something else to happen.
* This CAN sleep, such as for summoning rituals which poll for ghosts.
*
* Arguments
- * * user - the mob who did the ritual
+ * * user - the mob who did the ritual
* * selected_atoms - an list of atoms chosen as a part of this ritual.
* * loc - the turf the ritual's occuring on
*
@@ -158,9 +177,14 @@
var/obj/item/stack/sac_stack = sacrificed
var/how_much_to_use = 0
for(var/requirement in required_atoms)
- if(istype(sacrificed, requirement))
- how_much_to_use = min(required_atoms[requirement], sac_stack.amount)
- break
+ // If it's not requirement type and type is not a list, skip over this check
+ if(!istype(sacrificed, requirement) && !islist(requirement))
+ continue
+ // If requirement *is* a list and the stack *is* in the list, skip over this check
+ if(islist(requirement) && !is_type_in_list(sacrificed, requirement))
+ continue
+ how_much_to_use = min(required_atoms[requirement], sac_stack.amount)
+ break
sac_stack.use(how_much_to_use)
continue
@@ -264,7 +288,7 @@
/datum/heretic_knowledge/mark
abstract_parent_type = /datum/heretic_knowledge/mark
mutually_exclusive = TRUE
- cost = 2
+ cost = 1
/// The status effect typepath we apply on people on mansus grasp.
var/datum/status_effect/eldritch/mark_type
@@ -514,7 +538,7 @@
animate(summoned, 10 SECONDS, alpha = 155)
message_admins("A [summoned.name] is being summoned by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(summoned)].")
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [summoned.real_name]?", ROLE_HERETIC, FALSE, 10 SECONDS, summoned)
+ var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a [summoned.name]?", ROLE_HERETIC, FALSE, 10 SECONDS, summoned)
if(!LAZYLEN(candidates))
loc.balloon_alert(user, "ritual failed, no ghosts!")
animate(summoned, 0.5 SECONDS, alpha = 0)
@@ -634,8 +658,7 @@
to_chat(user, span_hypnophrase(span_big("[drain_message]")))
desc += " (Completed!)"
log_heretic_knowledge("[key_name(user)] completed a [name] at [worldtime2text()].")
- add_event_to_buffer(user, data = "completed a [name] at [worldtime2text()].", log_key = "HERETIC")
- user.add_mob_memory(/datum/memory/heretic_knowlege_ritual)
+ user.add_mob_memory(/datum/memory/heretic_knowledge_ritual)
return TRUE
#undef KNOWLEDGE_RITUAL_POINTS
diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm
index 97786c60e4c0..baf33b9f9bba 100644
--- a/code/modules/antagonists/heretic/influences.dm
+++ b/code/modules/antagonists/heretic/influences.dm
@@ -115,7 +115,7 @@
icon = 'icons/effects/eldritch.dmi'
icon_state = "pierced_illusion"
anchored = TRUE
- interaction_flags_atom = INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND
+ interaction_flags_atom = INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND|INTERACT_ATOM_NO_FINGERPRINT_INTERACT
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
alpha = 0
@@ -194,7 +194,7 @@
name = "reality smash"
icon = 'icons/effects/eldritch.dmi'
anchored = TRUE
- interaction_flags_atom = INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND
+ interaction_flags_atom = INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND|INTERACT_ATOM_NO_FINGERPRINT_INTERACT
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
invisibility = INVISIBILITY_OBSERVER
/// Whether we're currently being drained or not.
@@ -205,12 +205,28 @@
var/list/datum/mind/minds = list()
/// The image shown to heretics
var/image/heretic_image
+ /// We hold the turf we're on so we can remove and add the 'no prints' flag.
+ var/turf/on_turf
/obj/effect/heretic_influence/Initialize(mapload)
. = ..()
GLOB.reality_smash_track.smashes += src
heretic_image = image(icon, src, real_icon_state, OBJ_LAYER)
generate_name()
+ on_turf = get_turf(src)
+ if(!istype(on_turf))
+ return
+ on_turf.interaction_flags_atom |= INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND
+ RegisterSignal(on_turf, COMSIG_TURF_CHANGE, PROC_REF(replace_our_turf))
+
+/obj/effect/heretic_influence/proc/replace_our_turf(datum/source, path, new_baseturfs, flags, post_change_callbacks)
+ SIGNAL_HANDLER
+ post_change_callbacks += CALLBACK(src, PROC_REF(replace_our_turf_two))
+ on_turf = null //hard del ref?
+
+/obj/effect/heretic_influence/proc/replace_our_turf_two(turf/new_turf)
+ new_turf.interaction_flags_atom |= INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND
+ on_turf = new_turf
/obj/effect/heretic_influence/Destroy()
GLOB.reality_smash_track.smashes -= src
@@ -218,6 +234,8 @@
remove_mind(heretic)
heretic_image = null
+ on_turf?.interaction_flags_atom &= ~INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND
+ on_turf = null
return ..()
/obj/effect/heretic_influence/attack_hand_secondary(mob/user, list/modifiers)
@@ -239,7 +257,8 @@
// Using a codex will give you two knowledge points for draining.
if(!being_drained && istype(weapon, /obj/item/codex_cicatrix))
var/obj/item/codex_cicatrix/codex = weapon
- codex.open_animation()
+ if(!codex.book_open)
+ codex.attack_self(user) // open booke
INVOKE_ASYNC(src, PROC_REF(drain_influence), user, 2)
return TRUE
@@ -254,16 +273,13 @@
being_drained = TRUE
balloon_alert(user, "draining influence...")
- RegisterSignal(user, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
if(!do_after(user, 10 SECONDS, src))
being_drained = FALSE
balloon_alert(user, "interrupted!")
- UnregisterSignal(user, COMSIG_ATOM_EXAMINE)
return
// We don't need to set being_drained back since we delete after anyways
- UnregisterSignal(user, COMSIG_ATOM_EXAMINE)
balloon_alert(user, "influence drained")
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
@@ -286,19 +302,6 @@
GLOB.reality_smash_track.num_drained++
qdel(src)
-/*
- * Signal proc for [COMSIG_ATOM_EXAMINE], registered on the user draining the influence.
- *
- * Gives a chance for examiners to see that the heretic is interacting with an infuence.
- */
-/obj/effect/heretic_influence/proc/on_examine(atom/source, mob/user, list/examine_list)
- SIGNAL_HANDLER
-
- if(prob(50))
- return
-
- examine_list += span_warning("[source]'s hand seems to be glowing a [span_hypnophrase("strange purple")]...")
-
/*
* Add a mind to the list of tracked minds,
* making another person able to see us.
diff --git a/code/modules/antagonists/heretic/items/forbidden_book.dm b/code/modules/antagonists/heretic/items/forbidden_book.dm
index 84cd42462e0f..f389d0efa666 100644
--- a/code/modules/antagonists/heretic/items/forbidden_book.dm
+++ b/code/modules/antagonists/heretic/items/forbidden_book.dm
@@ -7,6 +7,8 @@
icon_state = "book"
worn_icon_state = "book"
w_class = WEIGHT_CLASS_SMALL
+ /// Helps determine the icon state of this item when it's used on self.
+ var/book_open = FALSE
/obj/item/codex_cicatrix/Initialize(mapload)
. = ..()
@@ -27,13 +29,21 @@
. += span_notice("Can be used to tap influences for additional knowledge points.")
. += span_notice("Can also be used to draw or remove transmutation runes with ease.")
+ . += span_notice("Additionally, it can work as a focus for your spells in a pinch, though a more specialized relic is recommended, as this may get dropped in combat.")
/obj/item/codex_cicatrix/attack_self(mob/user, modifiers)
. = ..()
if(.)
return
- open_animation()
+ if(book_open)
+ close_animation()
+ RemoveElement(/datum/element/heretic_focus)
+ w_class = WEIGHT_CLASS_SMALL
+ else
+ open_animation()
+ AddElement(/datum/element/heretic_focus)
+ w_class = WEIGHT_CLASS_NORMAL
/obj/item/codex_cicatrix/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
@@ -45,21 +55,19 @@
return
if(isopenturf(target))
- heretic_datum.try_draw_rune(user, target, drawing_time = 12 SECONDS)
+ heretic_datum.try_draw_rune(user, target, drawing_time = 8 SECONDS)
return TRUE
/*
* Plays a little animation that shows the book opening and closing.
*/
/obj/item/codex_cicatrix/proc/open_animation()
+ book_open = TRUE
icon_state = "[base_icon_state]_open"
flick("[base_icon_state]_opening", src)
- addtimer(CALLBACK(src, PROC_REF(close_animation)), 5 SECONDS)
-
-/*
- * Plays a closing animation and resets the icon state.
- */
+/// Plays a closing animation and resets the icon state.
/obj/item/codex_cicatrix/proc/close_animation()
+ book_open = FALSE
icon_state = base_icon_state
flick("[base_icon_state]_closing", src)
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index 577f901d0212..b9ae51ad7f0c 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -34,6 +34,7 @@
bio = 20
fire = 20
acid = 20
+ wound = 20
/obj/item/clothing/suit/hooded/cultrobes/eldritch/examine(mob/user)
. = ..()
@@ -64,6 +65,7 @@
laser = 30
energy = 30
bomb = 15
+ wound = 10
/obj/item/clothing/head/hooded/cult_hoodie/void/Initialize(mapload)
. = ..()
@@ -89,15 +91,31 @@
laser = 30
energy = 30
bomb = 15
+ wound = 10
/obj/item/clothing/suit/hooded/cultrobes/void/Initialize(mapload)
. = ..()
-
create_storage(storage_type = /datum/storage/pockets/void_cloak)
+ make_visible()
-/obj/item/clothing/suit/hooded/cultrobes/void/Initialize(mapload)
+/obj/item/clothing/suit/hooded/cultrobes/void/equipped(mob/user, slot)
. = ..()
- make_visible()
+ if(slot & ITEM_SLOT_OCLOTHING)
+ RegisterSignal(user, COMSIG_MOB_EQUIPPED_ITEM, PROC_REF(hide_item))
+ RegisterSignal(user, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(show_item))
+
+/obj/item/clothing/suit/hooded/cultrobes/void/dropped(mob/user)
+ . = ..()
+ UnregisterSignal(user, list(COMSIG_MOB_UNEQUIPPED_ITEM, COMSIG_MOB_EQUIPPED_ITEM))
+
+/obj/item/clothing/suit/hooded/cultrobes/void/proc/hide_item(obj/item/item, slot)
+ SIGNAL_HANDLER
+ if(slot & ITEM_SLOT_SUITSTORE)
+ ADD_TRAIT(item, TRAIT_NO_STRIP, REF(src)) // i'd use examine hide but its a flag and yeah
+
+/obj/item/clothing/suit/hooded/cultrobes/void/proc/show_item(obj/item/item, slot)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(item, TRAIT_NO_STRIP, REF(src))
/obj/item/clothing/suit/hooded/cultrobes/void/examine(mob/user)
. = ..()
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index aef5fd3771a8..b07d052a0917 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -14,6 +14,8 @@
w_class = WEIGHT_CLASS_NORMAL
force = 20
throwforce = 10
+ wound_bonus = 5
+ bare_wound_bonus = 15
demolition_mod = 0.8
hitsound = 'sound/weapons/bladeslice.ogg'
armour_penetration = 35
@@ -114,3 +116,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/hunter_rifle.dm b/code/modules/antagonists/heretic/items/hunter_rifle.dm
index 7e213270b94e..f682b13c9b89 100644
--- a/code/modules/antagonists/heretic/items/hunter_rifle.dm
+++ b/code/modules/antagonists/heretic/items/hunter_rifle.dm
@@ -1,5 +1,5 @@
/// The max range we can zoom in on people from.
-#define MAX_LIONHUNTER_RANGE 16
+#define MAX_LIONHUNTER_RANGE 30
// The Lionhunter, a gun for heretics
// The ammo it uses takes time to "charge" before firing,
@@ -16,7 +16,7 @@
/obj/item/gun/ballistic/rifle/lionhunter/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/scope, range_modifier = 1.25)
+ AddComponent(/datum/component/scope, range_modifier = 3.2)
/obj/item/ammo_box/magazine/internal/boltaction/lionhunter
name = "lionhunter rifle internal magazine"
@@ -113,7 +113,7 @@
// BUT, if we're at a decent range and the target's a living mob,
// the projectile's been channel fired. It has full effects and homes in.
if(distance > min_distance && isliving(target) && iscarbon(user))
- loaded_projectile.damage *= 1.33
+ loaded_projectile.damage *= 2
loaded_projectile.stamina *= 2
loaded_projectile.knockdown = 0.5 SECONDS
loaded_projectile.stutter = 6 SECONDS
diff --git a/code/modules/antagonists/heretic/items/keyring.dm b/code/modules/antagonists/heretic/items/keyring.dm
new file mode 100644
index 000000000000..4d3a056bdcfa
--- /dev/null
+++ b/code/modules/antagonists/heretic/items/keyring.dm
@@ -0,0 +1,203 @@
+/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_inner_range = 1
+ light_outer_range = 2
+ 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
+ /// if true the heretic is teleported to a random airlock, nonheretics are sent to the target
+ var/inverted = FALSE
+
+/obj/effect/knock_portal/Initialize(mapload, target, invert = FALSE)
+ . = ..()
+ 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)
+ inverted = invert
+
+///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()
+ if(!isnull(destination) && !QDELING(destination))
+ QDEL_NULL(destination)
+
+ destination = null
+ 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 = (inverted ? !IS_HERETIC_OR_MONSTER(teleportee) : 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
+ /// are our created portals inverted? (heretics get sent to a random airlock, crew get sent to the target)
+ var/inverted = FALSE
+
+/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.")
+ . += span_hypnophrase("Ctrl-clicking the ID, makes the ID make inverted portals instead, which teleport you onto a random airlock onstation, while heathens are teleported to the destination.")
+
+/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)
+
+/obj/item/card/id/advanced/heretic/CtrlClick(mob/user)
+ . = ..()
+ if(!IS_HERETIC(user))
+ return
+ inverted = !inverted
+ balloon_alert(user, "[inverted ? "now" : "no longer"] creating inverted rifts")
+
+///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, inverted)
+ portal_two = new(get_turf(door1), door1, inverted)
+ 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 000000000000..9744662b48ff
--- /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/eldritch.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/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
index dcb23609da11..b72bd1e31340 100644
--- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
@@ -72,9 +72,9 @@
name = "Ashen Passage"
desc = "Grants you Ashen Passage, a silent but short range jaunt."
gain_text = "He knew how to walk between the planes."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/mark/ash_mark,
- /datum/heretic_knowledge/codex_cicatrix,
/datum/heretic_knowledge/summon/fire_shark,
/datum/heretic_knowledge/medallion,
)
@@ -127,6 +127,7 @@
The mask instills fear into heathens who witness it, causing stamina damage, hallucinations, and insanity. \
It can also be forced onto a heathen, to make them unable to take it off..."
gain_text = "The Nightwatcher was lost. That's what the Watch believed. Yet he walked the world, unnoticed by the masses."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/blade_upgrade/ash,
/datum/heretic_knowledge/reroll_targets,
@@ -165,6 +166,7 @@
If any victims afflicted are in critical condition, they will also instantly die."
gain_text = "The fire was inescapable, and yet, life remained in his charred body. \
The Nightwatcher was a particular man, always watching."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/ultimate/ash_final,
/datum/heretic_knowledge/summon/ashy,
@@ -188,12 +190,13 @@
route = PATH_ASH
/// A static list of all traits we apply on ascension.
var/static/list/traits_to_apply = list(
- TRAIT_RESISTHEAT,
+ TRAIT_BOMBIMMUNE,
TRAIT_NOBREATH,
+ TRAIT_NOFIRE,
TRAIT_RESISTCOLD,
+ TRAIT_RESISTHEAT,
TRAIT_RESISTHIGHPRESSURE,
TRAIT_RESISTLOWPRESSURE,
- TRAIT_NOFIRE,
)
/datum/heretic_knowledge/ultimate/ash_final/is_valid_sacrifice(mob/living/carbon/human/sacrifice)
@@ -223,6 +226,9 @@
existing_beam_spell.beam_duration *= 0.66 // Faster beams
existing_beam_spell.cooldown_time *= 0.66 // Lower cooldown
+ var/datum/action/cooldown/spell/aoe/fiery_rebirth/fiery_rebirth = locate() in user.actions
+ fiery_rebirth?.cooldown_time *= 0.16
+
user.client?.give_award(/datum/award/achievement/misc/ash_ascension, user)
if(length(traits_to_apply))
user.add_traits(traits_to_apply, MAGIC_TRAIT)
diff --git a/code/modules/antagonists/heretic/knowledge/blade_lore.dm b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
index c2e9ea90f503..137632611a2f 100644
--- a/code/modules/antagonists/heretic/knowledge/blade_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
@@ -13,6 +13,9 @@
* Mark of the Blade
* Ritual of Knowledge
* Realignment
+ * > Sidepaths:
+ * Lionhunter Rifle
+ *
* Stance of the Scarred Duelist
* > Sidepaths:
* Carving Knife
@@ -22,20 +25,20 @@
* Furious Steel
* > Sidepaths:
* Maid in the Mirror
- * Lionhunter Rifle
+ * Rust Charge
*
* Maelstrom of Silver
*/
/datum/heretic_knowledge/limited_amount/starting/base_blade
name = "The Cutting Edge"
desc = "Opens up the Path of Blades to you. \
- Allows you to transmute a knife with two bars of silver to create a Sundered Blade. \
+ Allows you to transmute a knife with two bars of silver or titanium to create a Sundered Blade. \
You can create up to five at a time."
gain_text = "Our great ancestors forged swords and practiced sparring on the eve of great battles."
next_knowledge = list(/datum/heretic_knowledge/blade_grasp)
required_atoms = list(
/obj/item/knife = 1,
- /obj/item/stack/sheet/mineral/silver = 2,
+ list(/obj/item/stack/sheet/mineral/silver, /obj/item/stack/sheet/mineral/titanium) = 2,
)
result_atoms = list(/obj/item/melee/sickly_blade/dark)
limit = 5 // It's the blade path, it's a given
@@ -101,10 +104,10 @@
towards your attacker. This effect can only trigger once every 20 seconds."
gain_text = "The footsoldier was known to be a fearsome duelist. \
Their general quickly appointed them as their personal Champion."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/limited_amount/risen_corpse,
/datum/heretic_knowledge/mark/blade_mark,
- /datum/heretic_knowledge/codex_cicatrix,
/datum/heretic_knowledge/armor,
)
cost = 1
@@ -224,7 +227,10 @@
During this process, you will rapidly regenerate stamina and quickly recover from stuns, however, you will be unable to attack. \
This spell can be cast in rapid succession, but doing so will increase the cooldown."
gain_text = "In the flurry of death, he found peace within himself. Despite insurmountable odds, he forged on."
- next_knowledge = list(/datum/heretic_knowledge/duel_stance)
+ next_knowledge = list(
+ /datum/heretic_knowledge/duel_stance,
+ /datum/heretic_knowledge/rifle,
+ )
spell_to_add = /datum/action/cooldown/spell/realignment
cost = 1
route = PATH_BLADE
@@ -239,6 +245,7 @@
you gain increased resistance to gaining wounds and resistance to batons."
gain_text = "In time, it was he who stood alone among the bodies of his former comrades, awash in blood, none of it his own. \
He was without rival, equal, or purpose."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/blade_upgrade/blade,
/datum/heretic_knowledge/reroll_targets,
@@ -368,10 +375,11 @@
at a target, dealing damage and causing bleeding."
gain_text = "Without thinking, I took the knife of a fallen soldier and threw with all my might. My aim was true! \
The Torn Champion smiled at their first taste of agony, and with a nod, their blades became my own."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/summon/maid_in_mirror,
/datum/heretic_knowledge/ultimate/blade_final,
- /datum/heretic_knowledge/rifle,
+ /datum/heretic_knowledge/spell/rust_charge,
)
spell_to_add = /datum/action/cooldown/spell/pointed/projectile/furious_steel
cost = 1
diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
index 02040dae989f..0a12b8c0ca86 100644
--- a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
@@ -31,7 +31,7 @@
desc = "Opens up the Path of Cosmos to you. \
Allows you to transmute a sheet of plasma and a knife into an Cosmic Blade. \
You can only create two at a time."
- gain_text = "It looked at the stars to guide himself."
+ gain_text = "A nebula appeared in the sky, its infernal birth shone upon me. This was the start of a great transcendence."
next_knowledge = list(/datum/heretic_knowledge/cosmic_grasp)
required_atoms = list(
/obj/item/knife = 1,
@@ -43,8 +43,8 @@
/datum/heretic_knowledge/cosmic_grasp
name = "Grasp of Cosmos"
desc = "Your Mansus Grasp will give people a star mark (cosmic ring) and create a cosmic field where you stand."
- gain_text = "The more he looked the more everything made sense. \
- The stars traced out the path forward to his home."
+ gain_text = "Some stars dimmed, others' magnitude increased. \
+ With newfound strength I could channel the nebula's power into myself."
next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_runes)
cost = 1
route = PATH_COSMIC
@@ -68,11 +68,11 @@
desc = "Grants you Cosmic Runes, a spell that creates two runes linked with eachother for easy teleportation. \
Only the entity activating the rune will get transported, and it can be used by anyone without a star mark. \
However, people with a star mark will get transported along with another person using the rune."
- gain_text = "When day came, the Sleeper got lost. \
- The sun outshone the stars, so he lost his guide."
+ gain_text = "The distant stars crept into my dreams, roaring and screaming without reason. \
+ I spoke, and heard my own words echoed back."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/mark/cosmic_mark,
- /datum/heretic_knowledge/codex_cicatrix,
/datum/heretic_knowledge/essence,
/datum/heretic_knowledge/summon/fire_shark,
)
@@ -85,8 +85,8 @@
desc = "Your Mansus Grasp now applies the Mark of Cosmos. The mark is triggered from an attack with your Cosmic Blade. \
When triggered, the victim is returned to the location where the mark was originally applied to them. \
They will then be paralyzed for 2 seconds."
- gain_text = "As the guide was lost he found a new. The energy increased as the gaze he threw. \
- He didn't know, but with focus, the Sleepers energy began to flow."
+ gain_text = "The Beast now whispered to me occasionally, only small tidbits of their circumstances. \
+ I can help them, I have to help them."
next_knowledge = list(/datum/heretic_knowledge/knowledge_ritual/cosmic)
route = PATH_COSMIC
mark_type = /datum/status_effect/eldritch/cosmic
@@ -102,8 +102,8 @@
will be forced to sleep for 4 seconds. When the victim is hit it also creates a beam that \
deals a bit of fire damage and damages the cells. \
The beam lasts a minute, until the beam is obstructed or until a new target has been found."
- gain_text = "He dreamed to know how the matter travelled from star to star. \
- He lost interest in wanting to find out."
+ gain_text = "After waking in a cold sweat I felt a palm on my scalp, a sigil burned onto me. \
+ My veins now emitted a strange purple glow, the Beast knows I will surpass its expectations."
next_knowledge = list(/datum/heretic_knowledge/spell/star_blast)
spell_to_add = /datum/action/cooldown/spell/touch/star_touch
cost = 1
@@ -113,7 +113,8 @@
name = "Star Blast"
desc = "Fires a projectile that moves very slowly and creates cosmic fields on impact. \
Anyone hit by the projectile will recieve burn damage, a knockdown, and give people in a three tile range a star mark."
- gain_text = "He didn't try, yet felt the call of the night's Creator."
+ gain_text = "The Beast was behind me now at all times, with each sacrifice words of affirmation coursed through me."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/blade_upgrade/cosmic,
/datum/heretic_knowledge/reroll_targets,
@@ -131,8 +132,8 @@
The combo is reset after two seconds without making an attack, \
or if you attack someone already marked. If you combo more than four attacks you will recieve, \
a cosmic trail and increase your combo timer up to ten seconds."
- gain_text = "As he ascended to be a watcher, he needed to gather knowledge. \
- He started to draw it at his home."
+ gain_text = "The Beast took my blades in their hand, I kneeled and felt a sharp pain. \
+ The blades now glistened with fragmented power. I fell to the ground and wept at the beast's feet."
next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_expansion)
route = PATH_COSMIC
/// Storage for the second target.
@@ -207,7 +208,8 @@
name = "Cosmic Expansion"
desc = "Grants you Cosmic Expansion, a spell that creates a 3x3 area of cosmic fields around you. \
Nearby beings will also receive a star mark."
- gain_text = "He was well known, so he had a lot of drawing to do to gather as much of the things he forgot."
+ gain_text = "The ground now shook beneath me. The Beast inhabited me, and their voice was intoxicating."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/ultimate/cosmic_final,
/datum/heretic_knowledge/eldritch_coin,
@@ -227,9 +229,10 @@
The Star Gazer is a strong ally who can even break down reinforced walls. \
The Star Gazer has an aura that will heal you and damage opponents. \
Star Touch can now teleport you to the Star Gazer when activated in your hand."
- gain_text = "The past is gone, the Star Gazer became a vessel to watch over the universe. \
- The Creator made this his path and he forgot his purpose. \
- THE TIME IS NOW, WITNESS MY ASCENSION, THE STAR GAZER HAS GAINED PURPOSE ONCE MORE!"
+ gain_text = "The Beast held out its hand, I grabbed hold and they pulled me to them. Their body was towering, but it seemed so small and feeble after all their tales compiled in my head. \
+ I clung on to them, they would protect me, and I would protect it. \
+ I closed my eyes with my head laid against their form. I was safe. \
+ WITNESS MY ASCENSION!"
route = PATH_COSMIC
/// A static list of command we can use with our mob.
var/static/list/star_gazer_commands = list(
@@ -249,10 +252,10 @@
/datum/heretic_knowledge/ultimate/cosmic_final/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
. = ..()
priority_announce("[generate_heretic_text()] A Star Gazer has arrived into the station, [user.real_name] has ascended! This station is the domain of the Cosmos! [generate_heretic_text()]","[generate_heretic_text()]", ANNOUNCER_SPANOMALIES)
- var/mob/living/basic/star_gazer/star_gazer_mob = new /mob/living/basic/star_gazer(loc)
+ var/mob/living/basic/heretic_summon/star_gazer/star_gazer_mob = new /mob/living/basic/heretic_summon/star_gazer(loc)
star_gazer_mob.maxHealth = INFINITY
star_gazer_mob.health = INFINITY
- user.AddElement(/datum/element/death_linked, star_gazer_mob)
+ user.AddComponent(/datum/component/death_linked, star_gazer_mob)
star_gazer_mob.AddComponent(/datum/component/obeys_commands, star_gazer_commands)
star_gazer_mob.AddComponent(/datum/component/damage_aura, range = 7, burn_damage = 0.5, simple_damage = 0.5, immune_factions = list(FACTION_HERETIC), current_owner = user)
star_gazer_mob.befriend(user)
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index 84652904534f..210aa950c116 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -126,9 +126,9 @@
Voiceless Dead are mute ghouls and only have 50 health, but can use Bloody Blades effectively. \
You can only create two at a time."
gain_text = "I found notes of a dark ritual, unfinished... yet still, I pushed forward."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/mark/flesh_mark,
- /datum/heretic_knowledge/codex_cicatrix,
/datum/heretic_knowledge/void_cloak,
/datum/heretic_knowledge/medallion,
)
@@ -239,6 +239,7 @@
the ability to link minds to communicate with ease, but are very fragile and weak in combat."
gain_text = "I could not continue alone. I was able to summon The Uncanny Man to help me see more. \
The screams... once constant, now silenced by their wretched appearance. Nothing was out of reach."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/blade_upgrade/flesh,
/datum/heretic_knowledge/reroll_targets,
@@ -261,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)
@@ -268,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"
@@ -276,6 +280,7 @@
Stalkers can jaunt, release EMPs, shapeshift into animals or automatons, and are strong in combat."
gain_text = "I was able to combine my greed and desires to summon an eldritch beast I had never seen before. \
An ever shapeshifting mass of flesh, it knew well my goals. The Marshal approved."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/ultimate/flesh_final,
/datum/heretic_knowledge/summon/ashy,
diff --git a/code/modules/antagonists/heretic/knowledge/general_side.dm b/code/modules/antagonists/heretic/knowledge/general_side.dm
index b771108e272f..2dc2719227b1 100644
--- a/code/modules/antagonists/heretic/knowledge/general_side.dm
+++ b/code/modules/antagonists/heretic/knowledge/general_side.dm
@@ -39,19 +39,3 @@
return FALSE
return TRUE
-
-/datum/heretic_knowledge/codex_cicatrix
- name = "Codex Cicatrix"
- desc = "Allows you to transmute a bible, a fountain pen, and hide from an animal (or human) to create a Codex Cicatrix. \
- The Codex Cicatrix can be used when draining influences to gain additional knowledge, but comes at greater risk of being noticed. \
- It can also be used to draw and remove transmutation runes easier."
- gain_text = "The occult leaves fragments of knowledge and power anywhere and everywhere. The Codex Cicatrix is one such example. \
- Within the leather-bound faces and age old pages, a path into the Mansus is revealed."
- required_atoms = list(
- /obj/item/storage/book/bible = 1,
- /obj/item/pen/fountain = 1,
- /obj/item/stack/sheet/animalhide = 1,
- )
- result_atoms = list(/obj/item/codex_cicatrix)
- cost = 1
- route = PATH_SIDE
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 000000000000..f11ce9cc8f3e
--- /dev/null
+++ b/code/modules/antagonists/heretic/knowledge/knock_lore.dm
@@ -0,0 +1,230 @@
+/**
+ * # 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 box, 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."
+ adds_sidepath_points = 1
+ required_atoms = list(
+ /obj/item/storage/box = 1, //monkestation edit wallet ==> box (leather is too hard to get due to botany changes)
+ /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 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 = 1, //monkestation edit crayon/white ==> crayon (i checked the game code for this and i can't find a consistant spawn for it other than detective pockets)
+ /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."
+ adds_sidepath_points = 1
+ 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."
+ adds_sidepath_points = 1
+ 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/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index a05760f032ad..94cb319dee35 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -13,6 +13,9 @@
* Mark of Rust
* Ritual of Knowledge
* Rust Construction
+ * > Sidepaths:
+ * Lionhunter Rifle
+ *
* Aggressive Spread
* > Sidepaths:
* Curse of Corrosion
@@ -22,7 +25,7 @@
* Entropic Plume
* > Sidepaths:
* Rusted Ritual
- * Blood Cleave
+ * Rust Charge
*
* Rustbringer's Oath
*/
@@ -80,11 +83,12 @@
name = "Leeching Walk"
desc = "Grants you passive healing and resistance to batons while standing over rust."
gain_text = "The speed was unparalleled, the strength unnatural. The Blacksmith was smiling."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/mark/rust_mark,
- /datum/heretic_knowledge/codex_cicatrix,
/datum/heretic_knowledge/armor,
/datum/heretic_knowledge/essence,
+ /datum/heretic_knowledge/entropy_pulse,
)
cost = 1
route = PATH_RUST
@@ -155,7 +159,10 @@
Anyone overtop the wall will be throw aside (or upwards) and sustain damage."
gain_text = "Images of foreign and ominous structures began to dance in my mind. Covered head to toe in thick rust, \
they no longer looked man made. Or perhaps they never were in the first place."
- next_knowledge = list(/datum/heretic_knowledge/spell/area_conversion)
+ next_knowledge = list(
+ /datum/heretic_knowledge/spell/area_conversion,
+ /datum/heretic_knowledge/rifle,
+ )
spell_to_add = /datum/action/cooldown/spell/pointed/rust_construction
cost = 1
route = PATH_RUST
@@ -165,6 +172,7 @@
desc = "Grants you Aggressive Spread, a spell that spreads rust to nearby surfaces. \
Already rusted surfaces are destroyed."
gain_text = "All wise men know well not to visit the Rusted Hills... Yet the Blacksmith's tale was inspiring."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/blade_upgrade/rust,
/datum/heretic_knowledge/reroll_targets,
@@ -194,10 +202,11 @@
at friend or foe wildly. Also rusts and destroys and surfaces it hits."
gain_text = "The corrosion was unstoppable. The rust was unpleasable. \
The Blacksmith was gone, and you hold their blade. Champions of hope, the Rustbringer is nigh!"
+ adds_sidepath_points = 1
next_knowledge = list(
- /datum/heretic_knowledge/rifle,
/datum/heretic_knowledge/ultimate/rust_final,
/datum/heretic_knowledge/summon/rusty,
+ /datum/heretic_knowledge/spell/rust_charge,
)
spell_to_add = /datum/action/cooldown/spell/cone/staggered/entropic_plume
cost = 1
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index 9971142b35e2..27ebb54d88b2 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -187,7 +187,6 @@
LAZYADD(target_blacklist, sacrifice.mind)
heretic_datum.remove_sacrifice_target(sacrifice)
-
var/feedback = "Your patrons accept your offer"
var/sac_department_flag = sacrifice.mind?.assigned_role?.departments_bitflags | sacrifice.last_mind?.assigned_role?.departments_bitflags
if(sac_department_flag & DEPARTMENT_BITFLAG_COMMAND)
@@ -374,8 +373,11 @@
sac_target.remove_status_effect(/datum/status_effect/unholy_determination)
sac_target.reagents?.del_reagent(/datum/reagent/inverse/helgrasp/heretic)
sac_target.clear_mood_event("shadow_realm")
- sac_target.gain_trauma(/datum/brain_trauma/mild/phobia/supernatural, TRAUMA_RESILIENCE_MAGIC)
-
+ if(IS_HERETIC(sac_target))
+ var/datum/antagonist/heretic/victim_heretic = sac_target.mind?.has_antag_datum(/datum/antagonist/heretic)
+ victim_heretic.knowledge_points -= 3
+ else
+ sac_target.gain_trauma(/datum/brain_trauma/mild/phobia/heresy, TRAUMA_RESILIENCE_MAGIC)
// Wherever we end up, we sure as hell won't be able to explain
sac_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/slurring/heretic)
sac_target.adjust_stutter(40 SECONDS)
@@ -444,7 +446,10 @@
*/
/datum/heretic_knowledge/hunt_and_sacrifice/proc/after_return_live_target(mob/living/carbon/human/sac_target)
to_chat(sac_target, span_hypnophrase("The fight is over, but at great cost. You have been returned to the station in one piece."))
- to_chat(sac_target, span_big(span_hypnophrase("You don't remember anything leading up to the experience - All you can think about are those horrific hands...")))
+ if(IS_HERETIC(sac_target))
+ to_chat(sac_target, span_big(span_hypnophrase("You don't remember anything leading up to the experience, but you feel your connection with the Mansus weakened - Knowledge once known, forgotten...")))
+ else
+ to_chat(sac_target, span_big(span_hypnophrase("You don't remember anything leading up to the experience - All you can think about are those horrific hands...")))
// Oh god where are we?
sac_target.flash_act()
@@ -457,7 +462,10 @@
// Glad i'm outta there, though!
sac_target.add_mood_event("shadow_realm_survived", /datum/mood_event/shadow_realm_live)
- sac_target.add_mood_event("shadow_realm_survived_sadness", /datum/mood_event/shadow_realm_live_sad)
+ if(IS_HERETIC(sac_target))
+ sac_target.add_mood_event("shadow_realm_survived_sadness", /datum/mood_event/shadow_realm_live_sad_heretic)
+ else
+ sac_target.add_mood_event("shadow_realm_survived_sadness", /datum/mood_event/shadow_realm_live_sad)
// Could use a little pick-me-up...
sac_target.reagents?.add_reagent(/datum/reagent/medicine/atropine, 8)
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 0223d5c71e51..3988292f5374 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
@@ -42,6 +42,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"
@@ -114,3 +118,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/sacrifice_knowledge/sacrifice_moodlets.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_moodlets.dm
index 0bf6692f0924..48724b0d0911 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_moodlets.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_moodlets.dm
@@ -13,3 +13,8 @@
description = "The hands! The horrible, horrific hands! I see them when I close my eyes!"
mood_change = -6
timeout = 10 MINUTES
+
+/datum/mood_event/shadow_realm_live_sad_heretic
+ description = "I've been humiliated! My knowledge sapped from my being! The world feels much duller again..."
+ mood_change = -8
+ timeout = 8 MINUTES
diff --git a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
index 09c5f6cd8ebf..69258f2e7264 100644
--- a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
@@ -46,8 +46,8 @@
gain_text = "I met an old man in an anique shop who wielded a very unusual weapon. \
I could not purchase it at the time, but they showed me how they made it ages ago."
next_knowledge = list(
- /datum/heretic_knowledge/spell/furious_steel,
- /datum/heretic_knowledge/spell/entropic_plume,
+ /datum/heretic_knowledge/spell/realignment,
+ /datum/heretic_knowledge/spell/rust_construction,
/datum/heretic_knowledge/rifle_ammo,
)
required_atoms = list(
@@ -61,7 +61,7 @@
route = PATH_SIDE
/datum/heretic_knowledge/rifle_ammo
- name = "Lionhunter Rifle Ammunition"
+ name = "Lionhunter Rifle Ammunition (free)"
desc = "Allows you to transmute 3 ballistic ammo casings (used or unused) of any caliber, \
including shotgun shot, with any animal hide to create an extra clip of ammunition for the Lionhunter Rifle."
gain_text = "The weapon came with three rough iron balls, intended to be used as ammunition. \
@@ -72,7 +72,7 @@
/obj/item/ammo_casing = 3,
)
result_atoms = list(/obj/item/ammo_box/a762/lionhunter)
- cost = 1
+ cost = 0
route = PATH_SIDE
/// A list of calibers that the ritual will deny. Only ballistic calibers are allowed.
var/static/list/caliber_blacklist = list(
@@ -95,3 +95,15 @@
// We removed any invalid casings from the atoms list,
// return to allow the ritual to fill out selected atoms with the new list
return TRUE
+
+/datum/heretic_knowledge/spell/rust_charge
+ name = "Rust Charge"
+ desc = "A charge that must be started on a rusted tile and will destroy any rusted objects you come into contact with, will deal high damage to others and rust around you during the charge."
+ gain_text = "The hills sparkled now, as I neared them my mind began to wander. I quickly regained my resolve and pushed forward, this last leg would be the most treacherous."
+ next_knowledge = list(
+ /datum/heretic_knowledge/spell/furious_steel,
+ /datum/heretic_knowledge/spell/entropic_plume,
+ )
+ spell_to_add = /datum/action/cooldown/mob_cooldown/charge/rust
+ cost = 1
+ route = PATH_SIDE
diff --git a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm
index fa0b2621cbc5..f402722f9346 100644
--- a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm
@@ -5,7 +5,7 @@
desc = "Allows you to transmute a pool of ash, a liver, and a sheet of plasma into a Fire Shark. \
Fire Sharks are fast and strong in groups, but die quickly. They are also highly resistant against fire attacks. \
Fire Sharks inject phlogiston into its victims and spawn plasma once they die."
- gain_text = "My knowledge of the universe with the energy of remains, constructed it. It gave the Fire Shark life."
+ gain_text = "The cradle of the nebula was cold, but not dead. Light and heat flits even through the deepest darkness, and is hunted by its own predators."
next_knowledge = list(
/datum/heretic_knowledge/spell/cosmic_runes,
/datum/heretic_knowledge/spell/ash_passage,
@@ -15,7 +15,7 @@
/obj/item/organ/internal/liver = 1,
/obj/item/stack/sheet/mineral/plasma = 1,
)
- mob_to_summon = /mob/living/basic/fire_shark
+ mob_to_summon = /mob/living/basic/heretic_summon/fire_shark
cost = 1
route = PATH_SIDE
@@ -37,7 +37,7 @@
desc = "Allows you to transmute a sheet of plasma and a diamond to create an Eldritch Coin. \
The coin will open or close nearby doors when landing on heads and bolt or unbolt nearby doors \
when landing on tails. If the coin gets inserted into an airlock it emags the door destroying the coin."
- gain_text = "It tossed the coin and won its bet, now it gains..."
+ gain_text = "The Mansus is a place of all sorts of sins. But greed held a special role."
next_knowledge = list(
/datum/heretic_knowledge/spell/cosmic_expansion,
/datum/heretic_knowledge/spell/flame_birth,
diff --git a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
index 4a315575d61b..6439840fed5d 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 000000000000..97218ce5e941
--- /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/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
index 313412c030dc..482e46f804c1 100644
--- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
@@ -18,6 +18,28 @@
cost = 1
route = PATH_SIDE
+/datum/heretic_knowledge/entropy_pulse
+ name = "Pulse of Entropy"
+ desc = "Allows you to transmute 20 irons and 2 garbage items to fill the surrounding vicinity of the rune with rust."
+ gain_text = "Reality begins to whisper to me. To give it its entropic end."
+ required_atoms = list(
+ /obj/item/stack/sheet/iron = 20,
+ /obj/item/trash = 2
+ )
+ cost = 0
+ route = PATH_SIDE
+ var/rusting_range = 4
+
+/datum/heretic_knowledge/entropy_pulse/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
+ for(var/turf/nearby_turf in view(rusting_range, loc))
+ if(get_dist(nearby_turf, loc) <= 1) //tiles on rune should always be rusted
+ nearby_turf.rust_heretic_act()
+ //we exclude closed turf to avoid exposing cultist bases
+ if(prob(20) || isclosedturf(nearby_turf))
+ continue
+ nearby_turf.rust_heretic_act()
+ return TRUE
+
/datum/heretic_knowledge/curse/corrosion
name = "Curse of Corrosion"
desc = "Allows you to transmute wirecutters, a pool of vomit, and a heart to cast a curse of sickness on a crew member. \
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index 2e6b8b22772f..fc0a35aa4ac0 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -214,3 +214,79 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
spell_to_add = /datum/action/cooldown/spell/shadow_cloak
cost = 0
route = PATH_START
+
+/**
+ * Codex Cicatrixi is available at the start:
+ * This allows heretics to choose if they want to rush all the influences and take them stealthily, or
+ * Construct a codex and take what's left with more points.
+ * Another downside to having the book is strip searches, which means that it's not just a free nab, at least until you get exposed - and when you do, you'll probably need the faster drawing speed.
+ * Overall, it's a tradeoff between speed and stealth or power.
+ */
+/datum/heretic_knowledge/codex_cicatrix
+ name = "Codex Cicatrix"
+ desc = "Allows you to transmute a book, any unique pen (anything but generic pens), and your pick from any carcass (animal or human), leather, or hide to create a Codex Cicatrix. \
+ The Codex Cicatrix can be used when draining influences to gain additional knowledge, but comes at greater risk of being noticed. \
+ It can also be used to draw and remove transmutation runes easier, and as a spell focus in a pinch."
+ gain_text = "The occult leaves fragments of knowledge and power anywhere and everywhere. The Codex Cicatrix is one such example. \
+ Within the leather-bound faces and age old pages, a path into the Mansus is revealed."
+ required_atoms = list(
+ /obj/item/book = 1,
+ /obj/item/pen = 1,
+ list(/mob/living, /obj/item/stack/sheet/leather, /obj/item/stack/sheet/animalhide) = 1,
+ )
+ banned_atom_types = list(/obj/item/pen)
+ result_atoms = list(/obj/item/codex_cicatrix)
+ cost = 1
+ route = PATH_START
+ priority = MAX_KNOWLEDGE_PRIORITY - 3 // Least priority out of the starting knowledges, as it's an optional boon.
+
+/datum/heretic_knowledge/codex_cicatrix/parse_required_item(atom/item_path, number_of_things)
+ if(item_path == /obj/item/pen)
+ return "unique type of pen"
+ return ..()
+
+/datum/heretic_knowledge/codex_cicatrix/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ for(var/mob/living/body in atoms)
+ if(body.stat != DEAD)
+ continue
+
+ selected_atoms += body
+ return TRUE
+ return FALSE
+
+/datum/heretic_knowledge/codex_cicatrix/cleanup_atoms(list/selected_atoms)
+ var/mob/living/body = locate() in selected_atoms
+ if(!body)
+ return
+ // A golem or an android doesn't have skin!
+ var/exterior_text = "skin"
+ // If carbon, it's the limb. If not, it's the body.
+ var/ripped_thing = body
+
+ // We will check if it's a carbon's body.
+ // If it is, we will damage a random bodypart, and check that bodypart for its body type, to select between 'skin' or 'exterior'.
+ if(iscarbon(body))
+ var/mob/living/carbon/carbody = body
+ var/obj/item/bodypart/bodypart = pick(carbody.bodyparts)
+ ripped_thing = bodypart
+ bodypart.receive_damage(25, sharpness = SHARP_EDGED)
+ if(!(bodypart.bodytype & BODYTYPE_ORGANIC))
+ exterior_text = "exterior"
+ else
+ // If it is not a carbon mob, we will just check biotypes and damage it directly.
+ if(body.mob_biotypes & (MOB_MINERAL|MOB_ROBOTIC))
+ exterior_text = "exterior"
+ body.apply_damage(25, BRUTE)
+
+ // Procure book for flavor text. This is why we call parent at the end.
+ var/obj/item/book/le_book = locate() in selected_atoms
+ if(!le_book)
+ stack_trace("Somehow, no book in codex cicatrix selected atoms! [english_list(selected_atoms)]")
+ playsound(body, 'sound/items/poster_ripped.ogg', 100, TRUE)
+ body.do_jitter_animation()
+ body.visible_message(span_danger("An awful ripping sound is heard as [ripped_thing]'s [exterior_text] is ripped straight out, wrapping around [le_book || "the book"], turning into an eldritch shade of blue!"))
+ return ..()
diff --git a/code/modules/antagonists/heretic/knowledge/void_lore.dm b/code/modules/antagonists/heretic/knowledge/void_lore.dm
index 003ec1b4838b..8e9847f02ae7 100644
--- a/code/modules/antagonists/heretic/knowledge/void_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/void_lore.dm
@@ -81,9 +81,9 @@
You can still take damage due to a lack of pressure."
gain_text = "I found a thread of cold breath. It lead me to a strange shrine, all made of crystals. \
Translucent and white, a depiction of a nobleman stood before me."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/mark/void_mark,
- /datum/heretic_knowledge/codex_cicatrix,
/datum/heretic_knowledge/void_cloak,
/datum/heretic_knowledge/limited_amount/risen_corpse,
)
@@ -127,6 +127,7 @@
Additionally causes damage to heathens around your original and target destination."
gain_text = "The entity calls themself the Aristocrat. They effortlessly walk through air like \
nothing - leaving a harsh, cold breeze in their wake. They disappear, and I am left in the blizzard."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/blade_upgrade/void,
/datum/heretic_knowledge/reroll_targets,
@@ -161,6 +162,7 @@
desc = "Grants you Void Pull, a spell that pulls all nearby heathens towards you, stunning them briefly."
gain_text = "All is fleeting, but what else stays? I'm close to ending what was started. \
The Aristocrat reveals themselves to me again. They tell me I am late. Their pull is immense, I cannot turn back."
+ adds_sidepath_points = 1
next_knowledge = list(
/datum/heretic_knowledge/ultimate/void_final,
/datum/heretic_knowledge/spell/cleave,
diff --git a/code/modules/antagonists/heretic/magic/apetravulnera.dm b/code/modules/antagonists/heretic/magic/apetravulnera.dm
new file mode 100644
index 000000000000..801104dddf9f
--- /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
index f1d6de56e399..f458fb8b374a 100644
--- a/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm
+++ b/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm
@@ -25,7 +25,7 @@
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)
+ monster.AddElement(/datum/element/wall_tearer)
/datum/action/cooldown/spell/shapeshift/eldritch/ascension/do_unshapeshift(mob/living/caster)
. = ..()
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 000000000000..7bb6960354ec
--- /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 000000000000..29fcecf076fb
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/caretaker.dm
@@ -0,0 +1,45 @@
+/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/before_cast(atom/cast_on)
+ . = ..()
+ if(. & SPELL_CANCEL_CAST)
+ return
+
+ for(var/mob/living/alive in orange(5, owner))
+ if(alive.stat != DEAD && alive.client)
+ owner.balloon_alert(owner, "other minds nearby!")
+ return . | SPELL_CANCEL_CAST
+
+/datum/action/cooldown/spell/caretaker/cast(mob/living/cast_on)
+ . = ..()
+
+ 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/rust_charge.dm b/code/modules/antagonists/heretic/magic/rust_charge.dm
new file mode 100644
index 000000000000..0d693b0de86e
--- /dev/null
+++ b/code/modules/antagonists/heretic/magic/rust_charge.dm
@@ -0,0 +1,49 @@
+// Rust charge, a charge action that can only be started on rust (and only destroys rust tiles)
+/datum/action/cooldown/mob_cooldown/charge/rust
+ name = "Rust Charge"
+ desc = "A charge that must be started on a rusted tile and will destroy any rusted objects you come into contact with, will deal high damage to others and rust around you during the charge. As it is the rust that empoweres you for this ability, no focus is needed"
+ charge_distance = 10
+ charge_damage = 50
+ cooldown_time = 45 SECONDS
+
+/datum/action/cooldown/mob_cooldown/charge/rust/Activate(atom/target_atom)
+ var/turf/open/start_turf = get_turf(owner)
+ if(!istype(start_turf) || !HAS_TRAIT(start_turf, TRAIT_RUSTY))
+ return FALSE
+ StartCooldown(135 SECONDS, 135 SECONDS)
+ charge_sequence(owner, target_atom, charge_delay, charge_past)
+ StartCooldown()
+ return TRUE
+/datum/action/cooldown/mob_cooldown/charge/rust/on_move(atom/source, atom/new_loc, atom/target)
+ var/turf/victim = get_turf(owner)
+ if(!actively_moving)
+ return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+ new /obj/effect/temp_visual/decoy/fading(source.loc, source)
+ INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
+ victim.rust_heretic_act()
+ for(var/dir in GLOB.cardinals)
+ var/turf/nearby_turf = get_step(victim, dir)
+ if(istype(nearby_turf))
+ nearby_turf.rust_heretic_act()
+
+/datum/action/cooldown/mob_cooldown/charge/rust/DestroySurroundings(atom/movable/charger)
+ if(!destroy_objects)
+ return
+ for(var/dir in GLOB.cardinals)
+ var/turf/source = get_turf(owner)
+ var/turf/closed/next_turf = get_step(charger, dir)
+ if(!istype(source) || !istype(next_turf) || !HAS_TRAIT(source, TRAIT_RUSTY) || !HAS_TRAIT(next_turf, TRAIT_RUSTY))
+ continue
+ SSexplosions.medturf += next_turf
+
+/datum/action/cooldown/mob_cooldown/charge/rust/on_bump(atom/movable/source, atom/target)
+ if(owner == target)
+ return
+ if(destroy_objects)
+ if(isturf(target))
+ INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
+ if(isobj(target) && target.density)
+ SSexplosions.med_mov_atom += target
+
+ INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
+ hit_target(source, target, charge_damage)
diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm
index 2ad39f0b54fc..c7f0dfb47031 100644
--- a/code/modules/antagonists/heretic/magic/star_touch.dm
+++ b/code/modules/antagonists/heretic/magic/star_touch.dm
@@ -54,12 +54,12 @@
return target_turfs
/// To set the star gazer
-/datum/action/cooldown/spell/touch/star_touch/proc/set_star_gazer(mob/living/basic/star_gazer/star_gazer_mob)
+/datum/action/cooldown/spell/touch/star_touch/proc/set_star_gazer(mob/living/basic/heretic_summon/star_gazer/star_gazer_mob)
star_gazer = WEAKREF(star_gazer_mob)
/// To obtain the star gazer if there is one
/datum/action/cooldown/spell/touch/star_touch/proc/get_star_gazer()
- var/mob/living/basic/star_gazer/star_gazer_resolved = star_gazer?.resolve()
+ var/mob/living/basic/heretic_summon/star_gazer/star_gazer_resolved = star_gazer?.resolve()
if(star_gazer_resolved)
return star_gazer_resolved
return FALSE
@@ -73,11 +73,13 @@
/obj/item/melee/touch_attack/star_touch/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/effect_remover, \
+ AddComponent(\
+ /datum/component/effect_remover, \
success_feedback = "You remove %THEEFFECT.", \
tip_text = "Clear rune", \
on_clear_callback = CALLBACK(src, PROC_REF(after_clear_rune)), \
- effects_we_clear = list(/obj/effect/cosmic_rune))
+ effects_we_clear = list(/obj/effect/cosmic_rune), \
+ )
/*
* Callback for effect_remover component.
@@ -94,7 +96,7 @@
/obj/item/melee/touch_attack/star_touch/attack_self(mob/living/user)
var/datum/action/cooldown/spell/touch/star_touch/star_touch_spell = spell_which_made_us?.resolve()
- var/mob/living/basic/star_gazer/star_gazer_mob = star_touch_spell?.get_star_gazer()
+ var/mob/living/basic/heretic_summon/star_gazer/star_gazer_mob = star_touch_spell?.get_star_gazer()
if(!star_gazer_mob)
balloon_alert(user, "no linked star gazer!")
return ..()
@@ -229,7 +231,7 @@
/// What to add when the beam connects to a target
/datum/status_effect/cosmic_beam/proc/on_beam_hit(mob/living/target)
- if(!istype(target, /mob/living/basic/star_gazer))
+ if(!istype(target, /mob/living/basic/heretic_summon/star_gazer))
target.AddElement(/datum/element/effect_trail, /obj/effect/forcefield/cosmic_field/fast)
return
@@ -241,6 +243,6 @@
/// What to remove when the beam disconnects from a target
/datum/status_effect/cosmic_beam/proc/on_beam_release(mob/living/target)
- if(!istype(target, /mob/living/basic/star_gazer))
+ if(!istype(target, /mob/living/basic/heretic_summon/star_gazer))
target.RemoveElement(/datum/element/effect_trail, /obj/effect/forcefield/cosmic_field/fast)
return
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 000000000000..3b78b56ddc0b
--- /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 aeacc78909ee..caa083dfb8be 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -235,3 +235,59 @@
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, TRAIT_SECLUDED_LOCATION)
+
+/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))
+ RegisterSignal(owner, COMSIG_CARBON_CUFF_ATTEMPTED, PROC_REF(prevent_cuff))
+ 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)
+ UnregisterSignal(owner, COMSIG_CARBON_CUFF_ATTEMPTED)
+ 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
+
+/datum/status_effect/caretaker_refuge/proc/prevent_cuff(datum/source, mob/attemptee)
+ SIGNAL_HANDLER
+ return COMSIG_CARBON_CUFF_PREVENT
diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm
index 5f3f98029a15..f2f2d8a6da8c 100644
--- a/code/modules/antagonists/heretic/status_effects/debuffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm
@@ -168,7 +168,7 @@
return ..()
/datum/status_effect/star_mark/on_apply()
- if(istype(owner, /mob/living/basic/star_gazer))
+ if(istype(owner, /mob/living/basic/heretic_summon/star_gazer))
return FALSE
var/mob/living/spell_caster_resolved = spell_caster?.resolve()
var/datum/antagonist/heretic_monster/monster = owner.mind?.has_antag_datum(/datum/antagonist/heretic_monster)
@@ -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 057633688a18..0ffa0128f1fc 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
index f475ad90042f..02c4c739fa70 100644
--- a/code/modules/antagonists/heretic/structures/knock_final.dm
+++ b/code/modules/antagonists/heretic/structures/knock_final.dm
@@ -5,9 +5,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
icon = 'icons/obj/anomaly.dmi'
icon_state = "bhole3"
- color = COLOR_PURPLE
- light_color = COLOR_PURPLE
- light_outer_range = 20
+ color = COLOR_VOID_PURPLE
+ light_color = COLOR_VOID_PURPLE
+ light_inner_range = 20
+ light_outer_range = 30
anchored = TRUE
density = FALSE
layer = HIGH_PIPE_LAYER //0.01 above sigil layer used by heretic runes
@@ -20,8 +21,8 @@
var/static/list/monster_types
/// A static list of heretic summons which we should not create
var/static/list/monster_types_blacklist = list(
- /mob/living/basic/heretic_summon/armsy,
/mob/living/basic/heretic_summon/star_gazer,
+ /mob/living/basic/heretic_summon/armsy
)
/obj/structure/knock_tear/Initialize(mapload, datum/mind/ascendant_mind)
@@ -98,9 +99,10 @@
name = "destabilised tear"
icon = 'icons/obj/anomaly.dmi'
icon_state = "bhole3"
- color = COLOR_PURPLE
- light_color = COLOR_PURPLE
- light_outer_range = 20
+ color = COLOR_VOID_PURPLE
+ light_color = COLOR_VOID_PURPLE
+ light_inner_range = 15
+ light_outer_range = 25
layer = HIGH_PIPE_LAYER
duration = 1 SECONDS
@@ -111,4 +113,4 @@
animate(transform = matrix().Scale(0.2), time = 0.75 SECONDS)
animate(transform = matrix().Scale(3, 0), time = 0.1 SECONDS)
animate(src, color = COLOR_WHITE, time = 0.25 SECONDS, flags = ANIMATION_PARALLEL)
- animate(color = COLOR_PURPLE, time = 0.3 SECONDS)
+ animate(color = COLOR_VOID_PURPLE, time = 0.3 SECONDS)
diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm
index 60dcffdf5c19..bf4f0e18c7b6 100644
--- a/code/modules/antagonists/heretic/transmutation_rune.dm
+++ b/code/modules/antagonists/heretic/transmutation_rune.dm
@@ -90,6 +90,7 @@
// A copy of our requirements list.
// We decrement the values of to determine if enough of each key is present.
var/list/requirements_list = ritual.required_atoms.Copy()
+ var/list/banned_atom_types = ritual.banned_atom_types.Copy()
// A list of all atoms we've selected to use in this recipe.
var/list/selected_atoms = list()
@@ -105,8 +106,16 @@
// We already have enough of this type, skip
if(requirements_list[req_type] <= 0)
continue
- if(!istype(nearby_atom, req_type))
+ // If req_type is a list of types, check all of them for one match.
+ if(islist(req_type))
+ if(!(is_type_in_list(nearby_atom, req_type)))
+ continue
+ else if(!istype(nearby_atom, req_type))
continue
+ // if list has items, check if the strict type is banned.
+ if(length(banned_atom_types))
+ if(nearby_atom.type in banned_atom_types)
+ continue
// This item is a valid type. Add it to our selected atoms list.
selected_atoms |= nearby_atom
@@ -122,7 +131,7 @@
// All of the atoms have been checked, let's see if the ritual was successful
var/list/what_are_we_missing = list()
- for(var/atom/req_type as anything in requirements_list)
+ for(var/req_type in requirements_list)
var/number_of_things = requirements_list[req_type]
// <= 0 means it's fulfilled, skip
if(number_of_things <= 0)
@@ -130,10 +139,16 @@
// > 0 means it's unfilfilled - the ritual has failed, we should tell them why
// Lets format the thing they're missing and put it into our list
- var/formatted_thing = "[number_of_things] [initial(req_type.name)]\s"
- if(ispath(req_type, /mob/living/carbon/human))
- // If we need a human, there is a high likelihood we actually need a (dead) body
- formatted_thing = "[number_of_things] [number_of_things > 1 ? "bodies":"body"]"
+ var/formatted_thing = "[number_of_things] "
+ if(islist(req_type))
+ var/list/req_type_list = req_type
+ var/list/req_text_list = list()
+ for(var/atom/possible_type as anything in req_type_list)
+ req_text_list += ritual.parse_required_item(possible_type)
+ formatted_thing += english_list(req_text_list, and_text = "or")
+
+ else
+ formatted_thing = ritual.parse_required_item(req_type)
what_are_we_missing += formatted_thing
@@ -180,6 +195,7 @@
return ritual_result
+
/// A 3x3 heretic rune. The kind heretics actually draw in game.
/obj/effect/heretic_rune/big
icon = 'icons/effects/96x96.dmi'
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index 962c48b9fe78..357461303b92 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/basic/cult/constructs/_construct.dm b/code/modules/mob/living/basic/cult/constructs/_construct.dm
index 80f4ecf1bc6e..1cb7a68cb7da 100644
--- a/code/modules/mob/living/basic/cult/constructs/_construct.dm
+++ b/code/modules/mob/living/basic/cult/constructs/_construct.dm
@@ -43,7 +43,7 @@
var/can_repair_self = FALSE
/// Theme controls color. THEME_CULT is red THEME_WIZARD is purple and THEME_HOLY is blue
var/theme = THEME_CULT
- /// Can this construct smash walls? Gets the wall_smasher element if so.
+ /// Can this construct destroy walls?
var/smashes_walls = FALSE
/// The different flavors of goop constructs can drop, depending on theme.
var/static/list/remains_by_theme = list(
@@ -59,7 +59,7 @@
if(length(remains))
AddElement(/datum/element/death_drops, remains)
if(smashes_walls)
- AddElement(/datum/element/wall_smasher, strength_flag = ENVIRONMENT_SMASH_WALLS)
+ AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
if(can_repair)
AddComponent(\
/datum/component/healing_touch,\
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
index 3fcb60fb77c1..637f5b283b81 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
@@ -52,7 +52,7 @@
/mob/living/basic/gorilla/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP), ROUNDSTART_TRAIT)
- AddElement(/datum/element/wall_smasher)
+ AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
AddElement(/datum/element/dextrous)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_BAREFOOT)
AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = gorilla_food)
diff --git a/code/modules/mob/living/basic/heretic/ash_spirit.dm b/code/modules/mob/living/basic/heretic/ash_spirit.dm
index b2d4d8b4d294..d12ad5eb8d12 100644
--- a/code/modules/mob/living/basic/heretic/ash_spirit.dm
+++ b/code/modules/mob/living/basic/heretic/ash_spirit.dm
@@ -2,7 +2,7 @@
* Player-only mob which is fast, can jaunt a short distance, and is dangerous at close range
*/
/mob/living/basic/heretic_summon/ash_spirit
- name = "Ash Spirit"
+ name = "\improper Ash Spirit"
real_name = "Ashy"
desc = "A manifestation of ash, trailing a perpetual cloud of short-lived cinders."
icon_state = "ash_walker"
diff --git a/code/modules/mob/living/basic/heretic/fire_shark.dm b/code/modules/mob/living/basic/heretic/fire_shark.dm
index 48290b659546..b409d59805a5 100644
--- a/code/modules/mob/living/basic/heretic/fire_shark.dm
+++ b/code/modules/mob/living/basic/heretic/fire_shark.dm
@@ -1,16 +1,12 @@
-/mob/living/basic/fire_shark
- name = "fire shark"
+/mob/living/basic/heretic_summon/fire_shark
+ name = "\improper Fire Shark"
+ real_name = "Fire Shark"
desc = "It is a eldritch dwarf space shark, also known as a fire shark."
- icon = 'icons/mob/nonhuman-player/eldritch_mobs.dmi'
icon_state = "fire_shark"
icon_living = "fire_shark"
pass_flags = PASSTABLE | PASSMOB
istate = ISTATE_HARM | ISTATE_BLOCKING
mob_biotypes = MOB_ORGANIC | MOB_BEAST
- basic_mob_flags = DEL_ON_DEATH
- unsuitable_atmos_damage = 0
- unsuitable_cold_damage = 0
- unsuitable_heat_damage = 0
speed = -0.5
health = 16
maxHealth = 16
@@ -26,12 +22,9 @@
mob_size = MOB_SIZE_TINY
speak_emote = list("screams")
basic_mob_flags = DEL_ON_DEATH
- death_message = "implodes into itself."
- ai_controller = null
-/mob/living/basic/fire_shark/Initialize(mapload)
+/mob/living/basic/heretic_summon/fire_shark/Initialize(mapload)
. = ..()
- AddElement(/datum/element/death_drops, list(/obj/effect/gibspawner/human))
AddElement(/datum/element/death_gases, /datum/gas/plasma, 40)
AddElement(/datum/element/simple_flying)
AddElement(/datum/element/venomous, /datum/reagent/phlogiston, 2)
diff --git a/code/modules/mob/living/basic/heretic/flesh_stalker.dm b/code/modules/mob/living/basic/heretic/flesh_stalker.dm
index d12735206674..bd0c1e8c5cb5 100644
--- a/code/modules/mob/living/basic/heretic/flesh_stalker.dm
+++ b/code/modules/mob/living/basic/heretic/flesh_stalker.dm
@@ -1,6 +1,6 @@
/// Durable ambush mob with an EMP ability
/mob/living/basic/heretic_summon/stalker
- name = "Flesh Stalker"
+ name = "\improper Flesh Stalker"
real_name = "Flesh Stalker"
desc = "An abomination cobbled together from varied remains. Its appearance changes slightly every time you blink."
icon_state = "stalker"
diff --git a/code/modules/mob/living/basic/heretic/heretic_summon.dm b/code/modules/mob/living/basic/heretic/heretic_summon.dm
deleted file mode 100644
index d55b0a21ddf4..000000000000
--- a/code/modules/mob/living/basic/heretic/heretic_summon.dm
+++ /dev/null
@@ -1,32 +0,0 @@
-/mob/living/basic/heretic_summon
- name = "Eldritch Demon"
- real_name = "Eldritch Demon"
- desc = "A horror from beyond this realm, summoned by bad code."
- icon = 'icons/mob/nonhuman-player/eldritch_mobs.dmi'
- faction = list(FACTION_HERETIC)
- basic_mob_flags = DEL_ON_DEATH
- gender = NEUTER
- mob_biotypes = NONE
-
- habitable_atmos = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
- speed = 0
- melee_attack_cooldown = CLICK_CD_MELEE
-
- attack_sound = 'sound/weapons/punch1.ogg'
- response_help_continuous = "thinks better of touching"
- response_help_simple = "think better of touching"
- response_disarm_continuous = "flails at"
- response_disarm_simple = "flail at"
- response_harm_continuous = "rips"
- response_harm_simple = "tear"
- death_message = "implodes into itself."
-
- istate = ISTATE_HARM
- ai_controller = null
- speak_emote = list("screams")
- gold_core_spawnable = NO_SPAWN
-
-/mob/living/basic/heretic_summon/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/death_drops, string_list(list(/obj/effect/gibspawner/generic)))
diff --git a/code/modules/mob/living/basic/heretic/maid_in_the_mirror.dm b/code/modules/mob/living/basic/heretic/maid_in_the_mirror.dm
index edc5148b3ccc..26eaa9d7ebe8 100644
--- a/code/modules/mob/living/basic/heretic/maid_in_the_mirror.dm
+++ b/code/modules/mob/living/basic/heretic/maid_in_the_mirror.dm
@@ -1,6 +1,6 @@
/// Scout and assassin who can appear and disappear from glass surfaces. Damaged by being examined.
/mob/living/basic/heretic_summon/maid_in_the_mirror
- name = "Maid in the Mirror"
+ name = "\improper Maid in the Mirror"
real_name = "Maid in the Mirror"
desc = "A floating and flowing wisp of chilled air. Glancing at it causes it to shimmer slightly."
icon = 'icons/mob/simple/mob.dmi'
@@ -23,7 +23,7 @@
/// A list of REFs to people who recently examined us
var/list/recent_examiner_refs = list()
-/mob/living/basic/heretic_summon/Initialize(mapload)
+/mob/living/basic/heretic_summon/maid_in_the_mirror/Initialize(mapload)
. = ..()
var/static/list/loot = list(
/obj/effect/decal/cleanable/ash,
diff --git a/code/modules/mob/living/basic/heretic/raw_prophet.dm b/code/modules/mob/living/basic/heretic/raw_prophet.dm
index 967a7dacb605..23bbd749577d 100644
--- a/code/modules/mob/living/basic/heretic/raw_prophet.dm
+++ b/code/modules/mob/living/basic/heretic/raw_prophet.dm
@@ -4,7 +4,7 @@
* It can blind people to make a getaway, but also get stronger if it attacks the same target consecutively.
*/
/mob/living/basic/heretic_summon/raw_prophet
- name = "Raw Prophet"
+ name = "\improper Raw Prophet"
real_name = "Raw Prophet"
desc = "An abomination stitched together from a few severed arms and one swollen, orphaned eye."
icon_state = "raw_prophet"
diff --git a/code/modules/mob/living/basic/heretic/rust_walker.dm b/code/modules/mob/living/basic/heretic/rust_walker.dm
index c826fead48ca..a472cf5c654e 100644
--- a/code/modules/mob/living/basic/heretic/rust_walker.dm
+++ b/code/modules/mob/living/basic/heretic/rust_walker.dm
@@ -1,6 +1,6 @@
/// Pretty simple mob which creates areas of rust and has a rust-creating projectile spell
/mob/living/basic/heretic_summon/rust_walker
- name = "Rust Walker"
+ name = "\improper Rust Walker"
real_name = "Rusty"
desc = "A grinding, clanking construct which leaches life from its surroundings with every armoured step."
icon_state = "rust_walker_s"
diff --git a/code/modules/mob/living/basic/heretic/star_gazer.dm b/code/modules/mob/living/basic/heretic/star_gazer.dm
index 259d17d9d7d8..32729cc43229 100644
--- a/code/modules/mob/living/basic/heretic/star_gazer.dm
+++ b/code/modules/mob/living/basic/heretic/star_gazer.dm
@@ -1,14 +1,12 @@
-/mob/living/basic/star_gazer
- name = "Star Gazer"
+/mob/living/basic/heretic_summon/star_gazer
+ name = "\improper Star Gazer"
desc = "A creature that has been tasked to watch over the stars."
icon = 'icons/mob/nonhuman-player/96x96eldritch_mobs.dmi'
icon_state = "star_gazer"
icon_living = "star_gazer"
pixel_x = -32
base_pixel_x = -32
- basic_mob_flags = DEL_ON_DEATH
mob_biotypes = MOB_HUMANOID | MOB_EPIC
- faction = list(FACTION_HERETIC)
response_help_continuous = "passes through"
response_help_simple = "pass through"
speed = -0.2
@@ -16,9 +14,9 @@
health = 6000
obj_damage = 400
- melee_damage_lower = 35
- melee_damage_upper = 35
- istate = ISTATE_HARM | ISTATE_BLOCKING
+ armour_penetration = 20
+ melee_damage_lower = 40
+ melee_damage_upper = 40
sentience_type = SENTIENCE_BOSS
attack_verb_continuous = "ravages"
attack_verb_simple = "ravage"
@@ -29,11 +27,6 @@
damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
death_sound = 'sound/magic/cosmic_expansion.ogg'
- unsuitable_atmos_damage = 0
- unsuitable_cold_damage = 0
- unsuitable_heat_damage = 0
-
- gold_core_spawnable = NO_SPAWN
slowed_by_drag = FALSE
move_force = MOVE_FORCE_OVERPOWERING
move_resist = MOVE_FORCE_OVERPOWERING
@@ -45,7 +38,7 @@
ai_controller = /datum/ai_controller/basic_controller/star_gazer
-/mob/living/basic/star_gazer/Initialize(mapload)
+/mob/living/basic/heretic_summon/star_gazer/Initialize(mapload)
. = ..()
AddElement(/datum/element/death_drops, list(/obj/effect/temp_visual/cosmic_domain))
AddElement(/datum/element/death_explosion, 3, 6, 12)
diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling.dm b/code/modules/mob/living/basic/jungle/seedling/seedling.dm
index dfc20450a005..61a1a59f338a 100644
--- a/code/modules/mob/living/basic/jungle/seedling/seedling.dm
+++ b/code/modules/mob/living/basic/jungle/seedling/seedling.dm
@@ -77,7 +77,7 @@
petal_dead = mutable_appearance(icon, "[icon_state]_dead_overlay")
petal_dead.color = petal_color
- AddElement(/datum/element/wall_smasher)
+ AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
AddComponent(/datum/component/obeys_commands, seedling_commands)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
RegisterSignal(src, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(drop_can))
diff --git a/code/modules/mob/living/basic/lavaland/mook/mook.dm b/code/modules/mob/living/basic/lavaland/mook/mook.dm
index 1ceb75a4593f..13c9f23b4d2c 100644
--- a/code/modules/mob/living/basic/lavaland/mook/mook.dm
+++ b/code/modules/mob/living/basic/lavaland/mook/mook.dm
@@ -57,7 +57,7 @@
ore_overlay = mutable_appearance(icon, "mook_ore_overlay")
AddComponent(/datum/component/ai_listen_to_weather)
- AddElement(/datum/element/wall_smasher)
+ AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
RegisterSignal(src, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(drop_ore))
diff --git a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm
index 337a9f661f23..40debc7622bc 100644
--- a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm
+++ b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm
@@ -104,7 +104,7 @@
var/datum/callback/retaliate_callback = CALLBACK(src, PROC_REF(ai_retaliate_behaviour))
chosen_hat_colour = pick_weight(gnome_hat_colours)
apply_colour()
- AddElement(/datum/element/death_drops, list(/obj/effect/gibspawner/generic))
+ AddElement(/datum/element/death_drops, string_list(list(/obj/effect/gibspawner/generic)))
AddElement(/datum/element/footstep, FOOTSTEP_MOB_SHOE)
AddComponent(/datum/component/ai_retaliate_advanced, retaliate_callback)
AddComponent(/datum/component/swarming)
diff --git a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
index 549f352b990a..9497d8dd284d 100644
--- a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
+++ b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
@@ -63,7 +63,7 @@
add_traits(list(TRAIT_SPACEWALK, TRAIT_FREE_HYPERSPACE_MOVEMENT, TRAIT_NO_FLOATING_ANIM, TRAIT_HEALS_FROM_CARP_RIFTS), INNATE_TRAIT)
AddElement(/datum/element/simple_flying)
AddElement(/datum/element/content_barfer)
- AddElement(/datum/element/wall_tearer, do_after_key = DOAFTER_SOURCE_SPACE_DRAGON_INTERACTION)
+ AddElement(/datum/element/wall_tearer, tear_time = 4 SECONDS, reinforced_multiplier = 3, do_after_key = DOAFTER_SOURCE_SPACE_DRAGON_INTERACTION)
AddElement(/datum/element/door_pryer, pry_time = 4 SECONDS, interaction_key = DOAFTER_SOURCE_SPACE_DRAGON_INTERACTION)
AddComponent(/datum/component/seethrough_mob)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm
index d436e69b1af9..3d179e4f7332 100644
--- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm
+++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm
@@ -278,7 +278,7 @@
charge = new /datum/action/cooldown/mob_cooldown/charge/basic_charge()
charge.Grant(src)
- AddElement(/datum/element/tear_wall)
+ AddElement(/datum/element/wall_tearer)
AddElement(/datum/element/web_walker, /datum/movespeed_modifier/slow_web)
/mob/living/basic/spider/giant/tarantula/Destroy()
diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm
index 72226ec3e5de..98daeb6d8c23 100644
--- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm
+++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm
@@ -38,6 +38,6 @@
animal.melee_damage_lower = max((animal.melee_damage_lower * 2), 10)
animal.melee_damage_upper = max((animal.melee_damage_upper * 2), 10)
animal.transform *= 2
- animal.AddElement(/datum/element/wall_smasher, strength_flag = ENVIRONMENT_SMASH_RWALLS)
+ AddElement(/datum/element/wall_tearer)
to_chat(user, span_info("You increase the size of [animal], giving [animal.p_them()] a surge of strength!"))
qdel(src)
diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
index a9e2b538bdd7..12d73f132c97 100644
--- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
+++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
@@ -58,7 +58,7 @@
RegisterSignal(fugu, COMSIG_MOB_STATCHANGE, PROC_REF(check_death))
fugu.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/inflated)
ADD_TRAIT(fugu, TRAIT_FUGU_GLANDED, TRAIT_STATUS_EFFECT(id))
- fugu.AddElement(/datum/element/wall_smasher)
+ AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
fugu.mob_size = MOB_SIZE_LARGE
fugu.icon_state = "Fugu1"
fugu.melee_damage_lower = 15
@@ -76,7 +76,7 @@
UnregisterSignal(fugu, COMSIG_MOB_STATCHANGE)
fugu.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/inflated)
REMOVE_TRAIT(fugu, TRAIT_FUGU_GLANDED, TRAIT_STATUS_EFFECT(id))
- fugu.RemoveElement(/datum/element/wall_smasher)
+ fugu.RemoveElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
fugu.mob_size = MOB_SIZE_SMALL
fugu.melee_damage_lower = 0
fugu.melee_damage_upper = 0
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 6ca62821a3a8..2afd66ff58fb 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -92,7 +92,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/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index da38fb49fcb2..b72a363faaf9 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -48,7 +48,7 @@
button_icon_state = "spell_default"
overlay_icon_state = "bg_spell_border"
active_overlay_icon_state = "bg_spell_border_active_red"
- check_flags = AB_CHECK_CONSCIOUS
+ check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_PHASED
panel = "Spells"
melee_cooldown_time = 0 SECONDS
@@ -177,12 +177,7 @@
to_chat(owner, span_warning("Some form of antimagic is preventing you from casting [src]!"))
return FALSE
- if(!(spell_requirements & SPELL_CASTABLE_WHILE_PHASED) && HAS_TRAIT(owner, TRAIT_MAGICALLY_PHASED))
- if(feedback)
- to_chat(owner, span_warning("[src] cannot be cast unless you are completely manifested in the material plane!"))
- return FALSE
-
- if(!try_invoke(feedback = feedback))
+ if(!try_invoke(owner, feedback = feedback))
return FALSE
if(ishuman(owner))
diff --git a/code/modules/spells/spell_types/jaunt/_jaunt.dm b/code/modules/spells/spell_types/jaunt/_jaunt.dm
index e2fc4324ad97..e0463c02a10f 100644
--- a/code/modules/spells/spell_types/jaunt/_jaunt.dm
+++ b/code/modules/spells/spell_types/jaunt/_jaunt.dm
@@ -56,8 +56,8 @@
var/obj/effect/dummy/phased_mob/jaunt = new jaunt_type(loc_override || get_turf(jaunter), jaunter)
RegisterSignal(jaunt, COMSIG_MOB_EJECTED_FROM_JAUNT, PROC_REF(on_jaunt_exited))
- spell_requirements |= SPELL_CASTABLE_WHILE_PHASED
- jaunter.add_traits(list(TRAIT_MAGICALLY_PHASED, TRAIT_RUNECHAT_HIDDEN), REF(src))
+ check_flags &= ~AB_CHECK_PHASED
+ jaunter.add_traits(list(TRAIT_MAGICALLY_PHASED, TRAIT_RUNECHAT_HIDDEN, TRAIT_WEATHER_IMMUNE), REF(src))
// Don't do the feedback until we have runechat hidden.
// Otherwise the text will follow the jaunt holder, which reveals where our caster is travelling.
spell_feedback()
@@ -100,8 +100,8 @@
*/
/datum/action/cooldown/spell/jaunt/proc/on_jaunt_exited(obj/effect/dummy/phased_mob/jaunt, mob/living/unjaunter)
SHOULD_CALL_PARENT(TRUE)
- spell_requirements &= ~SPELL_CASTABLE_WHILE_PHASED
- unjaunter.remove_traits(list(TRAIT_MAGICALLY_PHASED, TRAIT_RUNECHAT_HIDDEN), REF(src))
+ check_flags |= AB_CHECK_PHASED
+ unjaunter.remove_traits(list(TRAIT_MAGICALLY_PHASED, TRAIT_RUNECHAT_HIDDEN, TRAIT_WEATHER_IMMUNE), REF(src))
// This needs to happen at the end, after all the traits and stuff is handled
SEND_SIGNAL(unjaunter, COMSIG_MOB_AFTER_EXIT_JAUNT, src)
diff --git a/code/modules/unit_tests/heretic_knowledge.dm b/code/modules/unit_tests/heretic_knowledge.dm
index 46545ef2ed50..f75fff24cee0 100644
--- a/code/modules/unit_tests/heretic_knowledge.dm
+++ b/code/modules/unit_tests/heretic_knowledge.dm
@@ -24,12 +24,12 @@
while(i < length(list_to_check))
var/datum/heretic_knowledge/path_to_create = list_to_check[++i]
if(!ispath(path_to_create))
- TEST_FAIL("Heretic Knowlege: Got a non-heretic knowledge datum (Got: [path_to_create]) in the list knowledges!")
+ TEST_FAIL("Heretic Knowledge: Got a non-heretic knowledge datum (Got: [path_to_create]) in the list knowledges!")
var/datum/heretic_knowledge/instantiated_knowledge = new path_to_create()
// Next knowledge is a list of typepaths.
for(var/datum/heretic_knowledge/next_knowledge as anything in instantiated_knowledge.next_knowledge)
if(!ispath(next_knowledge))
- TEST_FAIL("Heretic Knowlege: [next_knowledge.type] has a [isnull(next_knowledge) ? "null":"invalid path"] in its next_knowledge list!")
+ TEST_FAIL("Heretic Knowledge: [next_knowledge.type] has a [isnull(next_knowledge) ? "null":"invalid path"] in its next_knowledge list!")
continue
if(next_knowledge in list_to_check)
continue
@@ -44,7 +44,7 @@
// Unreachables is a list of typepaths - all paths that cannot be obtained.
var/list/unreachables = all_possible_knowledge - list_to_check
for(var/datum/heretic_knowledge/lost_knowledge as anything in unreachables)
- TEST_FAIL("Heretic Knowlege: [lost_knowledge] is unreachable by players! Add it to another knowledge's 'next_knowledge' list. If it is purposeful, set its route to 'null'.")
+ TEST_FAIL("Heretic Knowledge: [lost_knowledge] is unreachable by players! Add it to another knowledge's 'next_knowledge' list. If it is purposeful, set its route to 'null'.")
/*
diff --git a/code/modules/unit_tests/heretic_rituals.dm b/code/modules/unit_tests/heretic_rituals.dm
index 7298a1632749..4ac5bce8d3d3 100644
--- a/code/modules/unit_tests/heretic_rituals.dm
+++ b/code/modules/unit_tests/heretic_rituals.dm
@@ -63,6 +63,8 @@
var/list/created_atoms = list()
for(var/ritual_item_path in knowledge.required_atoms)
var/amount_to_create = knowledge.required_atoms[ritual_item_path]
+ if(islist(ritual_item_path))
+ ritual_item_path = pick(ritual_item_path)
for(var/i in 1 to amount_to_create)
created_atoms += new ritual_item_path(get_turf(our_heretic))
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index a0d7ccfc35c3..4442a805e9d6 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 55ad2d9a4155..71ebb4939439 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 40d4ea80df2d..8b7738f3b46a 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 dea5f15c738f..23570f8d9722 100755
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 a684dd1fbe62..747b57949be8 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 61d8948a98f6..c068036ecd4c 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 9d3f6e679d09..6d54ce63525d 100644
Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ
diff --git a/icons/obj/eldritch.dmi b/icons/obj/eldritch.dmi
index f3049a088cc5..9825139846b8 100644
Binary files a/icons/obj/eldritch.dmi and b/icons/obj/eldritch.dmi differ
diff --git a/icons/ui_icons/achievements/achievements.dmi b/icons/ui_icons/achievements/achievements.dmi
index ea6c4b79e07c..eb8ca71f3941 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 000000000000..87ca57302a28
Binary files /dev/null and b/sound/magic/hereticknock.ogg differ
diff --git a/strings/phobia.json b/strings/phobia.json
index 1ba55311c6d4..229c2acb5b43 100644
--- a/strings/phobia.json
+++ b/strings/phobia.json
@@ -85,6 +85,43 @@
"viva"
],
+ "heresy": [
+ "armsy",
+ "ash",
+ "blade",
+ "cloak",
+ "codex",
+ "cosmic",
+ "eldritch",
+ "fire shark",
+ "flesh",
+ "focus",
+ "ghoul",
+ "grasp",
+ "hand",
+ "heart",
+ "heresy",
+ "heretic",
+ "lionhunter",
+ "maid in the mirror",
+ "mansus",
+ "offer",
+ "pierced reality",
+ "raw prophet",
+ "reality crack",
+ "reality pierce",
+ "ritual",
+ "robe",
+ "rune",
+ "rust",
+ "sacrifice",
+ "space",
+ "stalker",
+ "star",
+ "void",
+ "worm"
+ ],
+
"lizards": [
"hiss",
"hissed",
@@ -210,7 +247,7 @@
"wiz",
"wizard",
"zombie"
- ],
+ ],
"aliens": [
"abductee",
diff --git a/tgstation.dme b/tgstation.dme
index 18c6d5f8b3a5..d4ded8ae2641 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1031,6 +1031,7 @@
#include "code\datums\components\damage_aura.dm"
#include "code\datums\components\damage_chain.dm"
#include "code\datums\components\deadchat_control.dm"
+#include "code\datums\components\death_linked.dm"
#include "code\datums\components\dejavu.dm"
#include "code\datums\components\deployable.dm"
#include "code\datums\components\direct_explosive_trap.dm"
@@ -1299,7 +1300,6 @@
#include "code\datums\elements\death_drops.dm"
#include "code\datums\elements\death_explosion.dm"
#include "code\datums\elements\death_gases.dm"
-#include "code\datums\elements\death_linked.dm"
#include "code\datums\elements\delete_on_drop.dm"
#include "code\datums\elements\deliver_first.dm"
#include "code\datums\elements\dextrous.dm"
@@ -1369,7 +1369,6 @@
#include "code\datums\elements\strippable.dm"
#include "code\datums\elements\structure_repair.dm"
#include "code\datums\elements\swabbable.dm"
-#include "code\datums\elements\tear_wall.dm"
#include "code\datums\elements\temporary_atom.dm"
#include "code\datums\elements\tenacious.dm"
#include "code\datums\elements\tiny_mob_hunter.dm"
@@ -2722,17 +2721,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"
@@ -2742,11 +2745,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"
@@ -2764,6 +2770,7 @@
#include "code\modules\antagonists\heretic\magic\mirror_walk.dm"
#include "code\modules\antagonists\heretic\magic\nightwatcher_rebirth.dm"
#include "code\modules\antagonists\heretic\magic\realignment.dm"
+#include "code\modules\antagonists\heretic\magic\rust_charge.dm"
#include "code\modules\antagonists\heretic\magic\rust_construction.dm"
#include "code\modules\antagonists\heretic\magic\rust_wave.dm"
#include "code\modules\antagonists\heretic\magic\shadow_cloak.dm"
@@ -2773,6 +2780,7 @@
#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\status_effects\buffs.dm"
#include "code\modules\antagonists\heretic\status_effects\debuffs.dm"
#include "code\modules\antagonists\heretic\status_effects\ghoul.dm"
@@ -4192,7 +4200,6 @@
#include "code\modules\mob\living\basic\heretic\fire_shark.dm"
#include "code\modules\mob\living\basic\heretic\flesh_stalker.dm"
#include "code\modules\mob\living\basic\heretic\flesh_worm.dm"
-#include "code\modules\mob\living\basic\heretic\heretic_summon.dm"
#include "code\modules\mob\living\basic\heretic\maid_in_the_mirror.dm"
#include "code\modules\mob\living\basic\heretic\raw_prophet.dm"
#include "code\modules\mob\living\basic\heretic\rust_walker.dm"
diff --git a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
index 34ed5f5ed077..41b4461ac236 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
@@ -51,6 +51,7 @@ type Objective = {
type Info = {
charges: number;
+ side_charges: number;
total_sacrifices: number;
ascended: BooleanLike;
objectives: Objective[];
@@ -160,7 +161,7 @@ const GuideSection = () => {
const InformationSection = (props, context) => {
const { data } = useBackend(context);
- const { charges, total_sacrifices, ascended } = data;
+ const { charges, side_charges, total_sacrifices, ascended } = data;
return (
@@ -182,6 +183,13 @@ const InformationSection = (props, context) => {
knowledge point{charges !== 1 ? 's' : ''}
+ {!!side_charges && (
+
+ {' '}
+ and {side_charges} side point
+ {side_charges !== 1 ? 's' : ''}
+
+ )}{' '}
.
@@ -278,7 +286,7 @@ const KnowledgeShop = (props, context) => {
const ResearchInfo = (props, context) => {
const { data } = useBackend(context);
- const { charges } = data;
+ const { charges, side_charges } = data;
return (
@@ -288,7 +296,14 @@ const ResearchInfo = (props, context) => {
You have {charges || 0}
knowledge point{charges !== 1 ? 's' : ''}
- {' '}
+
+ {!!side_charges && (
+
+ {' '}
+ and {side_charges} side point
+ {side_charges !== 1 ? 's' : ''}
+
+ )}{' '}
to spend.
diff --git a/tgui/packages/tgui/interfaces/ForbiddenLore.js b/tgui/packages/tgui/interfaces/ForbiddenLore.js
deleted file mode 100644
index 4d1f6db58a8c..000000000000
--- a/tgui/packages/tgui/interfaces/ForbiddenLore.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { sortBy } from 'common/collections';
-import { flow } from 'common/fp';
-import { useBackend } from '../backend';
-import { Box, Button, Section } from '../components';
-import { Window } from '../layouts';
-
-export const ForbiddenLore = (props, context) => {
- const { act, data } = useBackend(context);
- const { charges } = data;
- const to_know = flow([
- sortBy(
- (to_know) => to_know.state !== 'Research',
- (to_know) => to_know.path === 'Side'
- ),
- ])(data.to_know || []);
- return (
-
-
-
- Charges left : {charges}
- {to_know !== null ? (
- to_know.map((knowledge) => (
-
-
- {knowledge.path} path
-
-
-
-
- {knowledge.flavour}
-
- {knowledge.desc}
-
- ))
- ) : (
- No more knowledge can be found
- )}
-
-
-
- );
-};