diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp
index 8734cc41..6ab9c3d1 100644
--- a/src/engine/internal/engine.cpp
+++ b/src/engine/internal/engine.cpp
@@ -695,7 +695,14 @@ void Engine::setMousePressed(bool pressed)
 
 void Engine::clickTarget(Target *target)
 {
-    startHats(HatType::TargetClicked, {}, target);
+    assert(target);
+
+    if (target) {
+        if (!target->isStage() && !static_cast<Sprite *>(target)->visible())
+            return;
+
+        startHats(HatType::TargetClicked, {}, target);
+    }
 }
 
 unsigned int Engine::stageWidth() const
diff --git a/src/engine/virtualmachine_p.cpp b/src/engine/virtualmachine_p.cpp
index d6c93591..cdbc5c9a 100644
--- a/src/engine/virtualmachine_p.cpp
+++ b/src/engine/virtualmachine_p.cpp
@@ -121,6 +121,7 @@ VirtualMachinePrivate::VirtualMachinePrivate(VirtualMachine *vm, Target *target,
     regs = regsVector.data();
     loops.reserve(256);
     callTree.reserve(1024);
+    loopCountTree.reserve(1024);
 
     if (!rng)
         rng = RandomGenerator::instance().get();
@@ -205,7 +206,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
     assert(pos);
     unsigned int *loopStart;
     unsigned int *loopEnd;
-    size_t loopCount;
+    long loopCount;
     if (reset) {
         atEnd = false;
         noBreak = true;
@@ -233,10 +234,19 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
         warp = oldState.second;
         callTree.pop_back();
         procedureArgTree.pop_back();
+
         if (procedureArgTree.empty())
             procedureArgs = nullptr;
         else
             procedureArgs = &procedureArgTree.back();
+
+        int loopsToRemove = loops.size() - loopCountTree.back();
+        assert(loopsToRemove >= 0);
+        loopCountTree.pop_back();
+
+        for (int i = 0; i < loopsToRemove; i++)
+            loops.pop_back();
+
         DISPATCH();
     }
 
@@ -781,6 +791,14 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
             DISPATCH(); // this avoids freeing registers after "stopping" a warp script
         }
         FREE_REGS(ret);
+
+        if (atEnd) {
+            if (regCount > 0)
+                std::cout << "warning: VM: " << regCount << " registers were leaked by the script; this is most likely a bug in the VM or in the compiler" << std::endl;
+
+            return pos;
+        }
+
         DISPATCH();
     }
 
@@ -799,6 +817,7 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
             callTree.push_back({ ++pos, warp });
             procedureArgs = nextProcedureArgs;
             nextProcedureArgs = nullptr;
+            loopCountTree.push_back(loops.size());
             pos = procedurePos;
         } else
             pos++;
diff --git a/src/engine/virtualmachine_p.h b/src/engine/virtualmachine_p.h
index 7b1f1c6c..5ee86cf0 100644
--- a/src/engine/virtualmachine_p.h
+++ b/src/engine/virtualmachine_p.h
@@ -50,6 +50,7 @@ struct VirtualMachinePrivate
         std::vector<std::vector<Value>> procedureArgTree;
         std::vector<Value> *procedureArgs = nullptr;
         std::vector<Value> *nextProcedureArgs = nullptr;
+        std::vector<unsigned int> loopCountTree;
         bool noBreak = true;
         bool warp = false;
         bool stop = false;
diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp
index 73d83254..0fdc8dc4 100644
--- a/test/engine/engine_test.cpp
+++ b/test/engine/engine_test.cpp
@@ -1124,6 +1124,31 @@ TEST(EngineTest, TargetClickScripts)
     ASSERT_VAR(stage, "stage");
     ASSERT_EQ(GET_VAR(stage, "stage")->value().toInt(), 0);
 
+    // Hidden sprite
+    sprite = engine->targetAt(engine->findTarget("Hidden"));
+    ASSERT_TRUE(sprite);
+    engine->clickTarget(sprite);
+    engine->step();
+    ASSERT_VAR(stage, "1");
+    ASSERT_EQ(GET_VAR(stage, "1")->value().toInt(), 2);
+    ASSERT_VAR(stage, "2");
+    ASSERT_EQ(GET_VAR(stage, "2")->value().toInt(), 2);
+    ASSERT_VAR(stage, "hidden");
+    ASSERT_EQ(GET_VAR(stage, "hidden")->value().toInt(), 0);
+    ASSERT_VAR(stage, "stage");
+    ASSERT_EQ(GET_VAR(stage, "stage")->value().toInt(), 0);
+
+    engine->clickTarget(sprite);
+    engine->step();
+    ASSERT_VAR(stage, "1");
+    ASSERT_EQ(GET_VAR(stage, "1")->value().toInt(), 2);
+    ASSERT_VAR(stage, "2");
+    ASSERT_EQ(GET_VAR(stage, "2")->value().toInt(), 2);
+    ASSERT_VAR(stage, "hidden");
+    ASSERT_EQ(GET_VAR(stage, "hidden")->value().toInt(), 0);
+    ASSERT_VAR(stage, "stage");
+    ASSERT_EQ(GET_VAR(stage, "stage")->value().toInt(), 0);
+
     // Stage
     engine->clickTarget(stage);
     engine->step();
diff --git a/test/target_click_scripts.sb3 b/test/target_click_scripts.sb3
index ba8a0233..c30acb87 100644
Binary files a/test/target_click_scripts.sb3 and b/test/target_click_scripts.sb3 differ
diff --git a/test/virtual_machine/virtual_machine_test.cpp b/test/virtual_machine/virtual_machine_test.cpp
index 27819ba2..9308116e 100644
--- a/test/virtual_machine/virtual_machine_test.cpp
+++ b/test/virtual_machine/virtual_machine_test.cpp
@@ -1773,3 +1773,61 @@ TEST(VirtualMachineTest, NoCrashWhenReadingProcedureArgsAfterStopping)
     ASSERT_EQ(vm.registerCount(), 0);
     ASSERT_TRUE(vm.atEnd());
 }
+
+unsigned int killTest(VirtualMachine *vm)
+{
+    vm->kill();
+    return 1;
+}
+
+TEST(VirtualMachineTest, StopAfterKilled)
+{
+    // Regtest for #497
+    static unsigned int bytecode[] = { OP_START, OP_NULL, OP_EXEC, 0, OP_NULL, OP_NULL, OP_HALT };
+    static BlockFunc functions[] = { &killTest };
+
+    VirtualMachine vm;
+    vm.setBytecode(bytecode);
+    vm.setFunctions(functions);
+
+    vm.run();
+    ASSERT_TRUE(vm.atEnd());
+    ASSERT_EQ(vm.registerCount(), 0);
+}
+
+TEST(VirtualMachineTest, NoCrashAfterCallingProcedureFromLoop)
+{
+    // Regtest for #498
+    static unsigned int bytecode[] = { OP_START, OP_CONST, 0, OP_REPEAT_LOOP, OP_INIT_PROCEDURE, OP_CONST, 1, OP_ADD_ARG, OP_CALL_PROCEDURE, 0, OP_LOOP_END, OP_HALT };
+    static unsigned int procedure[] = { OP_START, OP_WARP, OP_CONST, 0, OP_REPEAT_LOOP, OP_READ_ARG, 0, OP_PRINT, OP_HALT, OP_LOOP_END, OP_HALT };
+    static unsigned int *procedures[] = { procedure };
+    static Value constValues[] = { 2, "test" };
+
+    VirtualMachine vm;
+    vm.setBytecode(bytecode);
+    vm.setProcedures(procedures);
+    vm.setConstValues(constValues);
+
+    ::testing::internal::CaptureStdout();
+    vm.run();
+    ASSERT_TRUE(vm.atEnd());
+    ASSERT_EQ(vm.registerCount(), 0);
+    ASSERT_EQ(::testing::internal::GetCapturedStdout(), "test\ntest\n");
+}
+
+TEST(VirtualMachineTest, NegativeLoopCount)
+{
+    // Regtest for #503
+    static unsigned int bytecode[] = { OP_START, OP_CONST, 0, OP_REPEAT_LOOP, OP_CONST, 1, OP_PRINT, OP_BREAK_FRAME, OP_LOOP_END, OP_HALT };
+    static Value constValues[] = { -1, "test" };
+
+    VirtualMachine vm;
+    vm.setBytecode(bytecode);
+    vm.setConstValues(constValues);
+
+    ::testing::internal::CaptureStdout();
+    vm.run();
+    ASSERT_TRUE(::testing::internal::GetCapturedStdout().empty());
+    ASSERT_TRUE(vm.atEnd());
+    ASSERT_EQ(vm.registerCount(), 0);
+}
