diff --git a/source/acmd_imports.hpp b/source/acmd_imports.hpp
index ea4e17f..fbfbdc8 100644
--- a/source/acmd_imports.hpp
+++ b/source/acmd_imports.hpp
@@ -16,4 +16,8 @@ namespace app::sv_animcmd {
 	extern u64 LAST_EFFECT_SET_RATE(u64) asm("_ZN3app10sv_animcmd20LAST_EFFECT_SET_RATEEP9lua_State") LINKABLE;
 }
 
+namespace app::sv_module_access {
+	extern u64 grab(u64) asm("_ZN3app16sv_module_access4grabEP9lua_State") LINKABLE;
+}
+
 #endif // ACMD_IMPORTS_H
diff --git a/source/hitbox_visualizer.hpp b/source/hitbox_visualizer.hpp
index e928f04..09bbb52 100644
--- a/source/hitbox_visualizer.hpp
+++ b/source/hitbox_visualizer.hpp
@@ -8,12 +8,14 @@
 #include "saltysd_helper.hpp"
 #include "const_value_table.h"
 #include "taunt_toggles.h"
+#include "raygun_printer.hpp"
 
 using namespace lib;
 using namespace app::lua_bind;
 using namespace app::sv_animcmd;
 
 void (*AttackModule_set_attack_lua_state)(u64, u64);
+u64 Catch_jumpback;
 
 Vector3f ID_COLORS[8] = { // used to tint the hitbox effects -- make sure that at least one component is equal to 1.0
 	{ 1.0f, 0.0f, 0.0f }, // #ff0000 (red)
@@ -47,6 +49,26 @@ namespace app::lua_bind::AttackModule {
 	}
 }
 
+namespace app::lua_bind::GrabModule {
+	// clear graphics every time we clear rebound
+	void set_rebound_replace(u64 module_accessor, bool rebound) {
+		if (is_training_mode() && rebound == false) {
+			// only if we're not shielding
+			int status_kind = StatusModule::status_kind(module_accessor);
+			if (!(status_kind >= FIGHTER_STATUS_KIND_GUARD_ON && status_kind <= FIGHTER_STATUS_KIND_GUARD_OFF)) {
+				Hash40 shieldEffectHash = { .hash = 0xAFAE75F05LL };
+				EffectModule::kill_kind(module_accessor, shieldEffectHash.hash, 0, 1);
+			}
+		}
+
+		// call original GrabModule::set_rebound_impl
+		u64 grab_module = load_module(module_accessor, 0x158);
+		void (*set_rebound)(u64, bool) = (void(*)(u64, bool))(load_module_impl(grab_module, 0x100));
+
+		return set_rebound(grab_module, rebound);
+	}
+}
+
 void generate_hitbox_effects(L2CAgent *l2c_agent, L2CValue *bone, L2CValue *size,
 		L2CValue *x, L2CValue *y, L2CValue *z, L2CValue *x2, L2CValue *y2, L2CValue *z2,
 		Vector3f *color) {
@@ -144,7 +166,7 @@ namespace app::sv_animcmd {
 				float kb = (percent_component * weight_component * 1.4f + 18.0f) * (kbg.raw * 0.01f) + bkb.raw;
 				color_scale = unlerp_bounded(50.0f, 200.0f, kb);
 			}
-			float color_t = 0.6f + 0.4f * powf(color_scale, 0.5f); // non-linear scaling to magnify differences at lower values
+			float color_t = 0.8f + 0.2f * powf(color_scale, 0.5f); // non-linear scaling to magnify differences at lower values
 			Vector3f color = color_lerp({ 1.0f, 1.0f, 1.0f }, ID_COLORS[id.raw % 8], color_t);
 		    generate_hitbox_effects(&l2c_agent, &bone, &size, &x, &y, &z, &x2, &y2, &z2, &color);
 		}
@@ -160,14 +182,55 @@ namespace app::sv_animcmd {
 		}
 		LOAD64(v1 + 16) = i;
 	}
+
+	void CATCH_replace(u64 a1) {
+		L2CAgent l2c_agent;
+		l2c_agent.L2CAgent_constr(a1);
+
+		// get all necessary grabbox params
+		L2CValue id, joint, size, x, y, z, x2, y2, z2;
+		l2c_agent.get_lua_stack(1, &id); // int
+		l2c_agent.get_lua_stack(2, &joint); // hash40
+		l2c_agent.get_lua_stack(3, &size); // float
+		l2c_agent.get_lua_stack(4, &x); // float
+		l2c_agent.get_lua_stack(5, &y); // float
+		l2c_agent.get_lua_stack(6, &z); // float
+		l2c_agent.get_lua_stack(7, &x2); // float or void
+		l2c_agent.get_lua_stack(8, &y2); // float or void
+		l2c_agent.get_lua_stack(9, &z2); // float or void
+
+		asm("MOV X9, %x0" : : "r"(Catch_jumpback));
+		asm("MOV X0, %x0" : : "r"(l2c_agent.lua_state_agent));
+
+		asm("SUB SP, SP, #0xC0");
+		asm("STR X25, [SP, #0x70]");
+		asm("STP X24, X23, [SP, #0x80]");
+		asm("STP X22, X21, [SP, #0x90]");
+		asm("BLR X9");
+
+		if (HITBOX_VIS && is_training_mode()) {
+			Vector3f color = ID_COLORS[(id.raw + 3) % 8];
+			generate_hitbox_effects(&l2c_agent, &joint, &size, &x, &y, &z, &x2, &y2, &z2, &color);
+		}
+	}
 }
 
 void hitbox_vis_main() {
 	AttackModule_set_attack_lua_state = (void (*)(u64, u64))SaltySDCore_FindSymbol("_ZN3app10sv_animcmd6ATTACKEP9lua_State") + 0xD0 - 0x70;
+	Catch_jumpback = SaltySDCore_FindSymbol("_ZN3app10sv_animcmd5CATCHEP9lua_State") + (4*4);
 	SaltySD_function_replace_sym(
 		"_ZN3app10sv_animcmd6ATTACKEP9lua_State",
 		(u64)&ATTACK_replace);
+
+	SaltySD_function_replace_sym(
+		"_ZN3app10sv_animcmd5CATCHEP9lua_State",
+		(u64)&CATCH_replace);
+
 	SaltySD_function_replace_sym(
 		"_ZN3app8lua_bind28AttackModule__clear_all_implEPNS_26BattleObjectModuleAccessorE",
 		(u64)&AttackModule::clear_all_replace);
+
+	SaltySD_function_replace_sym(
+		"_ZN3app8lua_bind28GrabModule__set_rebound_implEPNS_26BattleObjectModuleAccessorEb",
+		(u64)&GrabModule::set_rebound_replace);	
 }
diff --git a/source/script_replacement.hpp b/source/script_replacement.hpp
index 479b13b..0220126 100644
--- a/source/script_replacement.hpp
+++ b/source/script_replacement.hpp
@@ -39,13 +39,15 @@ u64 appeal_lw_replace(L2CAgent* l2c_agent, void* variadic) {
   
     acmd.frame(1);
     if (acmd.is_excute()) {
-		TOGGLE_STATE = (TOGGLE_STATE + 1) % NUM_TOGGLE_STATES;
-		if (TOGGLE_STATE == MASH_AIRDODGE)
-		    print_string(acmd.module_accessor, "MASH\nAIRDODGE");
-		else if (TOGGLE_STATE == MASH_JUMP)
-		    print_string(acmd.module_accessor, "MASH\nJUMP");
-		else
-		    print_string(acmd.module_accessor, "NONE");
+		if (is_training_mode()) {
+			TOGGLE_STATE = (TOGGLE_STATE + 1) % NUM_TOGGLE_STATES;
+			if (TOGGLE_STATE == MASH_AIRDODGE)
+				print_string(acmd.module_accessor, "MASH\nAIRDODGE");
+			else if (TOGGLE_STATE == MASH_JUMP)
+				print_string(acmd.module_accessor, "MASH\nJUMP");
+			else
+				print_string(acmd.module_accessor, "NONE");
+		}
     }
 
     return 0;
@@ -56,11 +58,13 @@ u64 appeal_hi_replace(L2CAgent* l2c_agent, void* variadic) {
 
 	acmd.frame(1);
 	if (acmd.is_excute()) {
-		HITBOX_VIS = !HITBOX_VIS;
-		if (HITBOX_VIS)
-			print_string(acmd.module_accessor, "HITBOX\nVIS");
-		else
-			print_string(acmd.module_accessor, "NO\nHITBOX");
+		if (is_training_mode()) {
+			HITBOX_VIS = !HITBOX_VIS;
+			if (HITBOX_VIS)
+				print_string(acmd.module_accessor, "HITBOX\nVIS");
+			else
+				print_string(acmd.module_accessor, "NO\nHITBOX");
+		}
 	}
 
 	return 0;
@@ -71,10 +75,12 @@ u64 appeal_s_replace(L2CAgent* l2c_agent, void* variadic) {
 
 	acmd.frame(1);
 	if (acmd.is_excute()) {
-		DI_STATE = (DI_STATE + 1) % NUM_DI_STATES;
-		const char* DI_strings[NUM_DI_STATES] = {"NONE", "AWAY", "DOWN AWAY", "DOWN", "DOWN IN",
-			"IN", "UP IN", "UP", "UP AWAY", "RANDOM\nIN AWAY"};
-		print_string(acmd.module_accessor, DI_strings[DI_STATE]);
+		if (is_training_mode()) {
+			DI_STATE = (DI_STATE + 1) % NUM_DI_STATES;
+			const char* DI_strings[NUM_DI_STATES] = {"NONE", "AWAY", "DOWN AWAY", "DOWN", "DOWN IN",
+				"IN", "UP IN", "UP", "UP AWAY", "RANDOM\nIN AWAY"};
+			print_string(acmd.module_accessor, DI_strings[DI_STATE]);
+		}
 	}
 
 	return 0;