From 8535bb846120a6b1bb0a7cbffe624019c9df5d31 Mon Sep 17 00:00:00 2001
From: HMBGERDO <61080616+HMBGERDO@users.noreply.github.com>
Date: Tue, 18 Feb 2025 13:50:39 +0100
Subject: [PATCH] [s] Shadow demon Shadow Crawl abuse fix (#28439)
* shadow demon shadow crawl fix
* var
---
code/__DEFINES/is_helpers.dm | 2 +
code/datums/spells/bloodcrawl.dm | 11 ++++++
.../demons/shadow_demon/shadow_demon.dm | 39 ++++++++++++++++++-
3 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 2e889278c1e..5f49361842e 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -45,6 +45,8 @@
#define ispulsedemon(A) (istype(A, /mob/living/simple_animal/demon/pulse_demon))
+#define isshadowdemon(A) (istype(A, /mob/living/simple_animal/demon/shadow))
+
// Objects
#define isobj(A) istype(A, /obj) //override the byond proc because it returns true on children of /atom/movable that aren't objs
diff --git a/code/datums/spells/bloodcrawl.dm b/code/datums/spells/bloodcrawl.dm
index 9bc2864a656..fec91009b90 100644
--- a/code/datums/spells/bloodcrawl.dm
+++ b/code/datums/spells/bloodcrawl.dm
@@ -273,6 +273,17 @@
action_background_icon_state = "shadow_demon_bg"
action_icon_state = "shadow_crawl"
+/datum/spell/bloodcrawl/shadow_crawl/can_cast(mob/user, charge_check, show_message)
+ var/mob/living/simple_animal/demon/shadow/current_demon = user
+ if(!istype(current_demon))
+ return ..()
+
+ if(current_demon.block_shadow_crawl)
+ to_chat(user, "You are too concentrated to activate [name].")
+ return FALSE
+
+ return ..()
+
/datum/spell/bloodcrawl/shadow_crawl/valid_target(turf/target, user)
return target.get_lumcount() < 0.2
diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm
index 25ce0ee4d66..ddc98d0d7b6 100644
--- a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm
+++ b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm
@@ -11,6 +11,10 @@
death_sound = 'sound/shadowdemon/shadowdeath.ogg'
var/thrown_alert = FALSE
var/wrapping = FALSE
+ /// Should only be TRUE if we're shooting the Shadow Grapple right now. If its TRUE, the demon wont be able to shadow crawl.
+ var/block_shadow_crawl = FALSE
+ /// Used for fault tolerance. If you set it manually, it may fuck up shooting_grapple var.
+ var/last_block_shadow_crawl = 0
/mob/living/simple_animal/demon/shadow/Login()
..()
@@ -66,6 +70,21 @@
target.forceMove(C)
wrapping = FALSE
+/mob/living/simple_animal/demon/shadow/proc/block_shadow_crawl()
+ last_block_shadow_crawl = world.time
+ block_shadow_crawl = TRUE
+ addtimer(CALLBACK(src, PROC_REF(check_block_shadow_crawl), last_block_shadow_crawl), 10 SECONDS)
+
+/mob/living/simple_animal/demon/shadow/proc/unblock_shadow_crawl()
+ last_block_shadow_crawl = 0
+ block_shadow_crawl = FALSE
+
+/mob/living/simple_animal/demon/shadow/proc/check_block_shadow_crawl(block_time)
+ if(block_time == last_block_shadow_crawl)
+ /// it means 10 seconds passed from last shadow grapple shot and it didnt unset block_shadow_crawl
+ to_chat(src, "You feel good enough to use Shadow Crawl again.")
+ unblock_shadow_crawl()
+
/obj/structure/shadowcocoon
name = "shadowy cocoon"
desc = "Something wrapped in what seems to be manifested darkness. Its surface distorts unnaturally, and it emanates deep shadows."
@@ -204,6 +223,9 @@
/obj/item/projectile/magic/shadow_hand/fire(setAngle)
if(firer)
+ var/mob/living/simple_animal/demon/shadow/current_demon = firer
+ if(istype(current_demon))
+ current_demon.block_shadow_crawl()
firer.Beam(src, icon_state = "grabber_beam", time = INFINITY, maxdistance = INFINITY, beam_sleep_time = 1, beam_type = /obj/effect/ebeam/floor)
return ..()
@@ -215,7 +237,12 @@
for(var/atom/extinguish_target in range(2, src))
extinguish_target.extinguish_light(TRUE)
if(!isliving(target))
- firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10)
+ if(isshadowdemon(firer))
+ firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10, callback = CALLBACK(firer, TYPE_PROC_REF(/mob/living/simple_animal/demon/shadow, unblock_shadow_crawl)))
+ else
+ firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10)
+ else
+ unblock_shadowdemon_crawl()
if(!.)
return
else
@@ -224,6 +251,16 @@
L.apply_damage(40, BRUTE, BODY_ZONE_CHEST)
L.throw_at(get_step(firer, get_dir(firer, target)), 50, 10)
+/obj/item/projectile/magic/shadow_hand/Destroy()
+ if(!hit)
+ unblock_shadowdemon_crawl()
+ return ..()
+
+/obj/item/projectile/magic/shadow_hand/proc/unblock_shadowdemon_crawl()
+ var/mob/living/simple_animal/demon/shadow/current_demon = firer
+ if(istype(current_demon))
+ current_demon.unblock_shadow_crawl()
+
/obj/effect/ebeam/floor
plane = FLOOR_PLANE