diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 49147dfe4d3..f313acf9ebc 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -110,6 +110,7 @@
// /atom/movable signals
#define COMSIG_MOVABLE_PRE_MOVE "movable_pre_move" //from base of atom/movable/Moved(): (/atom)
+ #define COMPONENT_MOVABLE_BLOCK_PRE_MOVE 1
#define COMSIG_MOVABLE_MOVED "movable_moved" //from base of atom/movable/Moved(): (/atom, dir)
#define COMSIG_MOVABLE_CROSS "movable_cross" //from base of atom/movable/Cross(): (/atom/movable)
#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable)
diff --git a/code/datums/components/tether.dm b/code/datums/components/tether.dm
new file mode 100644
index 00000000000..4ceba907afd
--- /dev/null
+++ b/code/datums/components/tether.dm
@@ -0,0 +1,37 @@
+/datum/component/tether
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ var/atom/tether_target
+ var/max_dist
+ var/tether_name
+
+/datum/component/tether/Initialize(atom/tether_target, max_dist = 4, tether_name)
+ if(!isliving(parent) || !istype(tether_target) || !tether_target.loc)
+ return COMPONENT_INCOMPATIBLE
+ src.tether_target = tether_target
+ src.max_dist = max_dist
+ if (ispath(tether_name, /atom))
+ var/atom/tmp = tether_name
+ src.tether_name = initial(tmp.name)
+ else
+ src.tether_name = tether_name
+ RegisterSignal(parent, list(COMSIG_MOVABLE_PRE_MOVE), .proc/checkTether)
+
+/datum/component/tether/proc/checkTether(mob/mover, newloc)
+ if (get_dist(mover,newloc) > max_dist)
+ to_chat(mover, "The [tether_name] runs out of slack and prevents you from moving!")
+ return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+
+ var/atom/blocker
+ :out
+ for(var/turf/T in getline(tether_target,newloc))
+ if (T.density)
+ blocker = T
+ break out
+ for(var/a in T)
+ var/atom/A = a
+ if(A.density && A != mover && A != tether_target)
+ blocker = A
+ break out
+ if (blocker)
+ to_chat(mover, "The [tether_name] catches on [blocker] and prevents you from moving!")
+ return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index e9c370154cb..bed96e11d9d 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -223,8 +223,10 @@
if(!newloc.Enter(src, src.loc))
return
+ if (SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE)
+ return
+
// Past this is the point of no return
- SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc)
var/atom/oldloc = loc
var/area/oldarea = get_area(oldloc)
var/area/newarea = get_area(newloc)
diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
index 2f07a61e9ca..497a0c3310f 100644
--- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
+++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
@@ -64,6 +64,7 @@
unsuitable_atmos_damage = 0
faction = list("hostile","vines","plants")
var/list/grasping = list()
+ var/list/tethers = list()
var/max_grasps = 4
var/grasp_chance = 20
var/grasp_pull_chance = 85
@@ -75,6 +76,8 @@
var/datum/beam/B = grasping[L]
if(B)
qdel(B)
+ for(var/datum/component/tether in tethers)
+ tether.RemoveComponent()
grasping = null
return ..()
@@ -96,24 +99,31 @@
step(L,get_dir(L,src)) //reel them in
L.Paralyze(60) //you can't get away now~
- if(grasping.len < max_grasps)
+ if(length(grasping) < max_grasps)
grasping:
for(var/mob/living/L in view(grasp_range, src))
- if(L == src || faction_check_mob(L) || (L in grasping) || L == target)
+ if(L == src || faction_check_mob(L) || L in grasping || L == target)
continue
- for(var/t in getline(src,L))
- for(var/a in t)
- var/atom/A = a
- if(A.density && A != L)
+ for(var/turf/T in getline(src,L))
+ if (T.density)
+ continue grasping
+ for(var/obj/O in T)
+ if(O.density)
continue grasping
if(prob(grasp_chance))
to_chat(L, "\The [src] has you entangled!")
grasping[L] = Beam(L, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine)
-
+ tethers += list(L.AddComponent(/datum/component/tether, src, grasp_range+1, /obj/effect/ebeam/vine), AddComponent(/datum/component/tether, L, grasp_range+1, /obj/effect/ebeam/vine))
break //only take 1 new victim per cycle
/mob/living/simple_animal/hostile/venus_human_trap/OpenFire(atom/the_target)
+ for(var/turf/T in getline(src,target))
+ if (T.density)
+ return
+ for(var/obj/O in T)
+ if(O.density)
+ return
var/dist = get_dist(src,the_target)
Beam(the_target, "vine", time=dist*2, maxdistance=dist+2, beam_type=/obj/effect/ebeam/vine)
the_target.attack_animal(src)
diff --git a/tgstation.dme b/tgstation.dme
index f32f294e0bd..c8e47347142 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -379,6 +379,7 @@
#include "code\datums\components\squeak.dm"
#include "code\datums\components\stationloving.dm"
#include "code\datums\components\swarming.dm"
+#include "code\datums\components\tether.dm"
#include "code\datums\components\thermite.dm"
#include "code\datums\components\uplink.dm"
#include "code\datums\components\waddling.dm"