uses SysUtils;

procedure CheckStuckThread(sec: integer);
var Timer: int64;  X, Y, Z: integer;
begin
  while (Engine.Status <> lsOnline) do delay(555);    // ожидаем входа в игру
  X:= User.X;  Y:= User.Y;  Z:= User.Z;               // запоминаем текущие координаты
  Timer:= GetTickCount + sec*1000;                    // запоминаем время

  while delay(555) do begin                           // запускаем бесконечный цикл
    if (Engine.Status = lsOnline) then begin          // если мы в игре, то 
      if (User.Cast.EndTime = 0)                      // если мы ничего не кастуем в данный момент
      and (not User.Sitting)                          // и не сидим регенимся
      and (Abs(User.X-X) < 10)                        // (значение 10 можно менять) 
      and (Abs(User.Y-Y) < 10) then begin             // и наши координаты почти не изменились, тогда
        Print('Похоже, что я стою на месте');         // пишем в логи (эту строчку можно закоментировать)
        if (GetTickCount > Timer) then begin          // если прошло больше указанного времени, то
          Script.Suspend;                             // тормозим скрипт
          Print('Кажется я застрял');                 // пишем в логи (эту строчку можно закоментировать)
          if Engine.UseItem(736) then begin           // используем SoE
            while (User.Cast.EndTime <> 0) do delay(555); // ждем окончания каста
            delay(5555);                              // и ждем еще немного, чтобы прогрузиться
          end;
          Script.Replace;                             // перезапускаем скрипт (или делаем чтобы-нибудь еще)
        end;
      end else begin                                  // если же мы ничего не кастуем, и наши координаты поменялись, то
        X:= User.X;  Y:= User.Y;  Z:= User.Z;         // запоминаем новые координаты
        Timer:= GetTickCount + sec*1000;              // обновляем таймер
        Print('Все в порядке, я двигаюсь');           // пишем в логи (эту строчку можно закоментировать)
      end;
    end;
  end;
end;

begin
  Script.NewThread(@CheckStuckThread, Pointer(30));  // параметром указываем время (в сек), после которого юзать СОЕ
  // code ...
end.

Общая логика такова:
Мы дожидаемся входа в игру, запоминаем свои координаты, и далее каждые пол секунды сравниваем свои координаты с теми, которые запомнили до этого: если координаты не изменились + мы ничего не кастуем в данный момент и это длится больше указанного времени (30 секунд в нашем случае), то тормозим скрипт, используем СОЕ и перезапускаем скрипт. Если же наши координаты хоть немного изменились или мы что-то кастуем (например то же СОЕ), то обнуляем таймер и запоминаем текущие координаты (с ними будем сравнивать дальше)