uses SysUtils;

procedure CheckStuckThread(sec: integer);
var Timer: int64;  X, Y, Z: integer;
begin
  while (Engine.Status <> lsOnline) do delay(555);    // expect to enter the game
  X:= User.X;  Y:= User.Y;  Z:= User.Z;               // remember current coordinates
  Timer:= GetTickCount + sec*1000;                    // remember the time

  while delay(555) do begin                           // run an endless loop
    if (Engine.Status = lsOnline) then begin          // if we're in the game then
      if (User.Cast.EndTime = 0)                      // if we don't cast anything at the moment
      and (not User.Sitting)                          // and do not sit
      and (Abs(User.X-X) < 10)                        // (value 10 can be changed) 
      and (Abs(User.Y-Y) < 10) then begin             // and our coordinates have hardly changed then
        Print('It looks like Im standing still');     // write to the logs (this line can be commented out)
        if (GetTickCount > Timer) then begin          // if more than a specified time has passed then
          Script.Suspend;                             // we slow down the script
          Print('I seem to be stuck');                // write to the logs (this line can be commented out)
          if Engine.UseItem(736) then begin           // use SoE
            while (User.Cast.EndTime <> 0) do delay(555); // waiting for the end of cast
            delay(5555);                              // and wait a little more to load
          end;
          Script.Replace;                             // restart the script (or do smth else)
        end;
      end else begin                                  // if we don't cast anything, and our coordinates have changed then
        X:= User.X;  Y:= User.Y;  Z:= User.Z;         // remember new coordinates
        Timer:= GetTickCount + sec*1000;              // update timer
        Print('All right im moving');                 // write to the logs (this line can be commented out)
      end;
    end;
  end;
end;

begin
  Script.NewThread(@CheckStuckThread, Pointer(30));  // specify the time parameter (in seconds) after which to use SOE
  // code ...
end.

The general logic is:
We wait for the entrance to the game, remember our coordinates, and then every half a second we compare our coordinates with those that we remembered before: if the coordinates have not changed + we do not cast anything at the moment and it lasts longer than the specified time (30 seconds in our case), then we slow down the script, use SOE and restart the script. If our coordinates have changed a little or we cast something (for example SOE), then reset the timer and remember the current coordinates (we will compare them further)