Forum in READ ONLY mode! All questions and discussions on Discord official server, invite link: https://discord.gg/VxsGzJ7

Ходилка с обходом препятствий

Archive messages
Post Reply
Edred
Moderator
Moderator
Posts: 559
Joined: 28.03.2006 21:29

Ходилка с обходом препятствий

Post by Edred »

ходилка для RC3 и предыдущих.

Эта тема восстановлена из кеша гугла. Если автор сможет повторить свой пост - я этот удалю.
Ходилка


Stealth Client Forum Forum Index -> Scripts
View previous topic :: View next topic
Author Message
Somebody
Neophyte



Joined: 15 Mar 2007
Posts: 19
Location: Odessa
Posted: Mon May 28, 2007 17:55 Post subject: Ходилка

--------------------------------------------------------------------------------

checkflow.inc
Code:
// checkflow.inc 30.06.2007
// написал Somebody. ICQ: 475728522 e-mail: [email protected]

var
CheckSWSTimer : TDateTime;

procedure InitCheckFlow;
begin
CheckSWSTimer := Now;
end;

procedure CheckSave;
var
t : TDateTime;
begin
if InJournalBetweenTimes('Saving World State in a minute', CheckSWSTimer, Now) = -1 then
begin
if InJournalBetweenTimes('Saving World State', CheckSWSTimer, Now) >= 0 then
begin
t := Now;
UOSay('q');
WaitJournalLine(t, 'q', 30000);
SetGlobal('stealth', 'Saving World State', '0');
end;
end
else SetGlobal('stealth', 'Saving World State', '1');
CheckSWSTimer := Now;
end;

function SWSSoon : boolean;
begin
result := (GetGlobal('stealth', 'Saving World State') = '1');
end;

procedure WaitConnection;
begin
if Connected then exit;
while not Connected do wait(100);
wait(3000);
end;

function CheckFlow : integer;
var
delay : integer;
begin
delay := timer;
CheckSave;
WaitConnection;
result := timer - delay;
end;


move.inc
Code:
// move.inc 30.06.2007
// написал Somebody. ICQ: 475728522 e-mail: [email protected]

// Реализация алгоритма "надёжной трассировки"
// http://pmg.org.ru/ai/stout.htm#robust_trace

const
FMoveArrMax = 100; // ставь здесь больше, если твой чар ходит на дистанции больше 13 тайлов или вокруг чара очень много препятствий. Если препятствий на пути мало, то можно оставить как есть.
var
PrognosisX, PrognosisY, FMoveArrCount : integer;
FMoveArr : array [1..FMoveArrMax] of array [1..2] of smallint;

function SetDirection(x, y : integer) : integer;
var
Dir : integer;
begin
if x < GetX(self) then
begin
if y > GetY(self) then Dir := 5;
if y = GetY(self) then Dir := 6;
if y < GetY(self) then Dir := 7;
end;
if x = GetX(self) then
begin
if y > GetY(self) then Dir := 4;
if y < GetY(self) then Dir := 0;
end;
if x > GetX(self) then
begin
if y > GetY(self) then Dir := 3;
if y = GetY(self) then Dir := 2;
if y < GetY(self) then Dir := 1;
end;
result := Dir;
end;

procedure CalcPrognosis(Dir : integer);
begin
if (Dir = 1) or (Dir = 2) or (Dir = 3) then PrognosisX := GetX(self) + 1;
if (Dir = 5) or (Dir = 6) or (Dir = 7) then PrognosisX := GetX(self) - 1;
if (Dir = 0) or (Dir = 4) then PrognosisX := GetX(self);

if (Dir = 3) or (Dir = 4) or (Dir = 5) then PrognosisY := GetY(self) + 1;
if (Dir = 7) or (Dir = 0) or (Dir = 1) then PrognosisY := GetY(self) - 1;
if (Dir = 2) or (Dir = 6) then PrognosisY := GetY(self);
end;

function TryToMove(Direction : integer; RunFlag : boolean) : boolean;
begin
if GetDirection(self) <> Direction then Raw_Move(Direction, RunFlag);
result := Raw_Move(Direction, RunFlag);
end;

function WrongMove(x, y : integer) : boolean;
var
i : integer;
begin
result := false;
if FMoveArrCount = 0 then exit;
for i := 1 to FMoveArrCount do
begin
if (x = FMoveArr[1]) and (y = FMoveArr[2]) then
begin
result := true;
exit;
end;
end;
end;

function sqr(_int : integer) : integer;
begin
result := _int * _int;
end;

function CalcDist(x, y : integer) : integer;
begin
result := trunc(sqrt(sqr(GetX(self) - x) + sqr(GetY(self) - y)));
end;

procedure SetWrongMove(x, y : integer);
begin
FMoveArrCount := FMoveArrCount + 1;
FMoveArr[FMoveArrCount][1] := x;
FMoveArr[FMoveArrCount][2] := y;
end;

function RewindDir(Dir, c : integer) : integer;
begin
result := Dir + c;
if result < 0 then result := result + 8;
if result > 7 then result := result - 8;
end;

function Move(x, y, tolerance : integer) : boolean;
var
Dir, Dist, lastX, lastY, t, i, timeout : integer;
begin
FMoveArrCount := 0;
Dist := CalcDist(x, y);
timeout := Dist * 5000; // 5 секунд времени на шаг
t := timer;
while true do
begin
Dist := CalcDist(x, y);
if Dist <= tolerance then // пришёл
begin
result := true;
exit;
end;
if timer - t > timeout then // провал по таймауту
begin
AddToSystemJournal('Move: Time moved out!');
result := false;
exit;
end;
Dir := SetDirection(x, y);
CalcPrognosis(Dir);
if WrongMove(PrognosisX, PrognosisY) then
begin
for i := 1 to 7 do
begin
Dir := RewindDir(Dir, 1);
CalcPrognosis(Dir);
if not WrongMove(PrognosisX, PrognosisY) then break;
end;
if i = 8 then
begin
AddToSystemJournal('Move: Cannot move');
result := false;
exit;
end;
end;
timeout := timeout + CheckFlow;
lastX := GetX(self); lastY := GetY(self);
if TryToMove(Dir, false) then SetWrongMove(lastX, lastY) // отсюда чар пришёл
else SetWrongMove(PrognosisX, PrognosisY);
end;
end;

procedure _move(x, y, tolerance : integer);
begin
while not Move(x, y, tolerance) do wait(100);
end;


Используется так:
Code:
{$Include 'checkflow.inc'}
{$Include 'move.inc'}
...
begin
InitCheckFlow;
...
move(1234, 1234, 0); // или _move(1234, 1234, 0);
...


move(x, y, tolerance);
tolerance - дистанция, на которую чар подойдёт к (x, y)

Пробуйте.
Кстати, я буду рад любым замечаниям и комментариям.

Last edited by Somebody on Wed May 30, 2007 19:59; edited 5 times in total

Back to top


Kilroy
Neophyte



Joined: 17 Mar 2007
Posts: 15

Posted: Tue May 29, 2007 0:24 Post subject:

--------------------------------------------------------------------------------

Чем она отличается от той ходилки которую вылаживали ранее?

Back to top


Somebody
Neophyte



Joined: 15 Mar 2007
Posts: 19
Location: Odessa
Posted: Tue May 29, 2007 17:15 Post subject:

--------------------------------------------------------------------------------

дай ссылку или копипастни сюда ходилку. Я сравню

Back to top


Edred
Moderator



Joined: 28 Mar 2006
Posts: 656
Location: Saint-Petersburg
Posted: Wed May 30, 2007 1:10 Post subject:

--------------------------------------------------------------------------------

Отлично! Наконец кто-то написал ходилку с обходом препятствий.

Надо бы вынести flag в отдельный параметр. То есть выбор бег/хотьба.

Back to top


DerMeister
Novice



Joined: 19 Nov 2006
Posts: 163

Posted: Wed May 30, 2007 2:29 Post subject:

--------------------------------------------------------------------------------

По какому принципу (алгоритму) она троссирует препятствие?
ПС: В пару слов, а то неохота моск себе нагружать разбором кода...
_________________
ЧК

Back to top


Somebody
Neophyte



Joined: 15 Mar 2007
Posts: 19
Location: Odessa
Posted: Wed May 30, 2007 2:43 Post subject:

--------------------------------------------------------------------------------

Edred,
1) "скорость при ходьбе > скорости при беге" - проверял.
2) при ходьбе он идёт плавнее, чем при беге, тоесть выглядит это лучше.

Вывод: Бессмысленный флаг до тех пор, пока Миралекс не доделает хождение. Как только чар сможет нормально бегать, флаг появится.

DerMeister,
шаг в сторону цели
Если невозможно, пометить координаты как заблокированные.
Если возможно, сделать шаг и пометить предыдущую точку как заблокированную.

XXXXX
X <--C
XXXXX

из такой штуки чар не выйдет. Он упрётся в углубление и move надо будет запустить заново.

PS: Я буду вносить некоторые изменения в код. Хорошо бы, чтобы модератор взялся обновлять пост. Эдред, поддержишь?

UPD (насчёт флага, если сильно нужен):
Code:
function TryToMove(Direction : integer; RunFlag : boolean) : boolean;
// меняем на
function TryToMove(Direction : integer) : boolean;

if TryToMove(Dir, false) then SetWrongMove(lastX, lastY)
// превращаем в
if TryToMove(Dir) then SetWrongMove(lastX, lastY)

// и где-нибудь вверху своего скрипта пишем:
const RunFlag = true; // или false


Back to top


Acronym
Apprentice



Joined: 10 May 2005
Posts: 217
Location: Odessa, NDW
Posted: Wed May 30, 2007 10:44 Post subject:

--------------------------------------------------------------------------------

Code:

procedure SetWrongMove(x, y : integer);
begin
if (x = 2084) and (y = 2078) then
***

2084,2078 - Что это? Заранее установленные непроходимые координаты?
_________________
Current version: Stealth 1.0 RC3

Back to top


Edred
Moderator



Joined: 28 Mar 2006
Posts: 656
Location: Saint-Petersburg
Posted: Wed May 30, 2007 13:24 Post subject:

--------------------------------------------------------------------------------

Введи номер версии скрипта. При каждом обновлении вноси в первый пост правку и изменяй номер версии. Плюс отписаться новым постом о изменениях.

Тему прилепил, чтобы не потерялась.

Back to top


Somebody
Neophyte



Joined: 15 Mar 2007
Posts: 19
Location: Odessa
Posted: Wed May 30, 2007 19:55 Post subject:

--------------------------------------------------------------------------------

вместо номера версии использую дату обновления.

30.06.2007:

- почистил скрипт от ненужного кода (в т.ч. от многих комментариев)
- добавил _move(x, y, tolerance : integer); - чар будет пытаться дойти, пока не дойдёт (либо зациклит скрипт)

В checkflow.inc есть функция SWSSoon:
Code:
CheckFlow;
while SWSSoon wait(1000); // возвращает true, если скоро SWS


Back to top


admir
Neophyte



Joined: 27 Sep 2007
Posts: 24

Posted: Sat Dec 01, 2007 2:34 Post subject:

--------------------------------------------------------------------------------

А спецом сделал дубликаты CHECKSAVE и WAITCONNECTION
чтоб погеморнее было ?
еще такой вопрос - допустим у меня чар идет вниз умирается в угол стола и все застревает на вечно там .
это связано с тем что стелс не меет обрабатываьт пакет с запретом на движение ?
тоесть по сути дела вся ходилка выше написанная не может обойти даже 1 тайл .

Back to top


ruff
Novice



Joined: 26 Sep 2007
Posts: 96
Location: Praha, CZ
Posted: Sat Dec 01, 2007 3:07 Post subject:

--------------------------------------------------------------------------------

admir wrote:

еще такой вопрос - допустим у меня чар идет вниз умирается в угол стола и все застревает на вечно там .
это связано с тем что стелс не меет обрабатываьт пакет с запретом на движение ?
тоесть по сути дела вся ходилка выше написанная не может обойти даже 1 тайл .

[::]|/\/\/\|[:]
%)
Застряет не на вечно, но с этой ходилкой да, т.к. она маркает пройденные клетки как инвалиды (во избежание зацикливания) - на узких коридорах чар оказываеца залочен, вперед инвалид по режекту сервера, назад - промаркано как пройденые. но в случае стола должен бы медленно поползти вдоль его края.
На самом деле есть такая трабла (высокий таймаут у Raw_Move), решаеца.
_________________
--
Looking forward to reading yours.

Back to top


admir
Neophyte



Joined: 27 Sep 2007
Posts: 24

Posted: Sat Dec 01, 2007 3:23 Post subject:

--------------------------------------------------------------------------------

я кстати заметил что при упирании в не проходимый тайл, Рав маут вырабатывает задержку - только зачем ?

как решить

Back to top


Acronym
Apprentice



Joined: 10 May 2005
Posts: 217
Location: Odessa, NDW
Posted: Sat Dec 01, 2007 23:25 Post subject:

--------------------------------------------------------------------------------

admir wrote:
я кстати заметил что при упирании в не проходимый тайл, Рав маут вырабатывает задержку - только зачем ?
как решить

Как решить: попробовать описать маршрут без подобных, непроходимых, тайлов.
_________________
Current version: Stealth 1.0 RC3

Back to top


SaNeK
Neophyte



Joined: 22 Mar 2007
Posts: 48
Location: Kiev
Posted: Wed Jan 16, 2008 21:25 Post subject:

--------------------------------------------------------------------------------

Ходилка хорошая
Предметы обходит всегда через южное направление.
Трабл: если персонаж мертв то некоторое время идет, а потом стопит скрипт. Проглянул не нашел в чем дело. Может подскажите, кто знает?
grundick
Developer
Developer
Posts: 272
Joined: 31.01.2008 21:16

Post by grundick »

Думаю, уместно будет сюда прилепить )
Вот вам его ходилка для RC4, Release 1.0 и последующих.
Тестите, критикуйте )
Есть одно но! Теперь не используется CheckFlow. у разных шардов разные системные сообщения при сейве, поэтоу не считаю целесообразным её подключать. Если я не прав - исправляюсь )

Не забываем про настройку шарда Check Diagonal Move! Важный параметр. Про него можете прочесть в разделе FAQ / Несколько слов о поиске пути.

Для самых ленивых напишу.
move идёт до цели с учётом таймаута. Изначально рассчитывается прямое расстояние до цели. Соответственно считается что чар совершит такое кол-во шагов. На каждый шаг выделяется время. (Сейчас это 3 сек, реально нужно подстраивать под условия) . Итого чару даётся времени = (кол-во шагов)*(время одного шага). Конечно это очень приближённый подсчет необходимого таймаута, потому что ходилка реально "не знает" длины пути. Тем не менее ,если время вышло, а цель не достигнута, move прекращает работу с сообщением 'Move: Time moved out!'
Функция _move тупо зацикливает работу функции move. Вот и всё различие.

Code: Select all

// написал Somebody. ICQ: 475728522 e-mail: [email protected]

// Реализация алгоритма "надёжной трассировки"
// http://pmg.org.ru/ai/stout.htm#robust_trace

const 
FMoveArrMax = 300; // ставь здесь больше, если твой чар ходит на дистанции больше 13 тайлов или вокруг чара очень много препятствий. Если препятствий на пути мало, то можно оставить как есть. 
var 
PrognosisX, PrognosisY, FMoveArrCount : integer; 
FMoveArr : array [1..FMoveArrMax] of array [1..2] of smallint; 

procedure LocalWaitConnection(WaitTime : Integer);
begin
if Connected then Exit;
while not Connected do Wait(1000);
{WaitTime - Waiting After Connected}
wait(WaitTime);
end;

Function Abs(A: integer): integer;
Begin
If A>=0 then result:=A 
Else result:=0-A;
End;

function SetDirection(x, y : integer) : integer; 
var
   MyX,MyY,DiffX,DiffY,GoDir: integer;
Begin
MyX:=GetX(self);
MyY:=GetY(self);
DiffX:=Abs(MyX-x);
DiffY:=Abs(MyY-y);

if (DiffX/(DiffY+0.1))>=2 then 
   begin
   if (MyX>X) then 
      GoDir:=6 
   else 
      GoDir:=2; 
   end  
else 
   
   if (DiffY/(DiffX+0.1))>=2 then 
      begin
      if (MyY>Y) then 
         GoDir:=0 
      else 
         GoDir:=4;
      end  
   else 
        
      if (MyX>X) and (MyY>Y) then  GoDir:=7
      else 

        if (MyX>X) and (MyY<Y) then  GoDir:=5 
        else 

           if (MyX<X) and (MyY>Y) then  GoDir:=1 
           else 

             if (MyX<X) and (MyY<Y) then  GoDir:=3; 
             
result:=GoDir;
end;


procedure CalcPrognosis(Dir : integer); 
begin 
   if (Dir = 1) or (Dir = 2) or (Dir = 3) then PrognosisX := GetX(self) + 1; 
   if (Dir = 5) or (Dir = 6) or (Dir = 7) then PrognosisX := GetX(self) - 1; 
   if (Dir = 0) or (Dir = 4) then PrognosisX := GetX(self); 

   if (Dir = 3) or (Dir = 4) or (Dir = 5) then PrognosisY := GetY(self) + 1; 
   if (Dir = 7) or (Dir = 0) or (Dir = 1) then PrognosisY := GetY(self) - 1; 
   if (Dir = 2) or (Dir = 6) then PrognosisY := GetY(self); 
end; 

function TryToMove(Direction : integer; RunFlag : boolean) : boolean; 
var 
  StepResult : byte;

begin 
   result:=false;
   wait(100);
   localWaitConnection(1000);     
   //While Not CheckLag(50) do Wait(500);   // checkLag не пашет!!! Визитера бить!!
 
   
   if IsWorldCellPassable(getX(self),getY(self),PrognosisX,PrognosisY,WorldNum,getZ(self)) then 
      begin
      if GetDirection(self) <> Direction then Step(Direction, RunFlag); 
      StepResult:=Step(Direction,RunFlag);
     // If (StepResult=5) OR (StepResult=1) then wait(5000);
      If StepResult=7 then result:=true;
      end;
   
   
end; 

// available StepResult
// 0 - Unknown Error
// 1 - Mover: Resync is pending, ignoring.
// 2 - Mover: Step buffer overflow, Ignoring
// 3 - Mover: Client Canceled. Point not Passable.
// 4 - Mover: Move timeout.
// 5 - Mover: MoveReject
// 6 - Mover: Client Check OK, send move request
// 7 - Mover: Server Check OK, move request accepted

function WrongMove(x, y : integer) : boolean; 
var 
i : integer; 
begin 
   result := false; 
   if FMoveArrCount = 0 then exit; 
   for i := 1 to FMoveArrCount do 
      begin 
      if (x = FMoveArr[i][1]) and (y = FMoveArr[i][2]) then 
         begin 
         result := true; 
         exit; 
         end; 
      end; 
end; 


function Min(x,y: integer): integer;
begin
 if x>y then Result:=y else Result:=x;
end;

function HEst(x,y: integer): integer;
var dx,dy,Ddx : integer;
begin
  dx:= GetX(self)-x;
  dy:= GetY(self)-y;
  Ddx:= dx-dy;
  If dx<0 then dx:=0-dx;
  If dy<0 then dy:=0-dy;
  If Ddx<0 then Ddx:=0-Ddx;
  Result:= min(dx,dy)+Ddx;
end;

procedure SetWrongMove(x, y : integer); 
begin 
   if FMoveArrCount>298 then FMoveArrCount:=0;  // обнуление массива непроходимых тайлов
   FMoveArrCount := FMoveArrCount + 1; 
   FMoveArr[FMoveArrCount][1] := x; 
   FMoveArr[FMoveArrCount][2] := y; 
end; 

function RewindDir(Dir, c : integer) : integer; 
begin 
   result := Dir + c; 
   if result < 0 then result := result + 8; 
   if result > 7 then result := result - 8; 
end; 

function Move(x, y, tolerance : integer; RunFlag: boolean) : boolean; 
var 
Dir, Dist, lastX, lastY, t, i, timeout : integer; 
begin 
   FMoveArrCount := 0; 
   Dist := HEst(x, y); 
   timeout := Dist * 3000; // 3 секунды времени на шаг 
   t := timer; 
   while true do 
      begin 
      Dist := HEst(x, y); 
      if Dist <= tolerance then // пришёл 
         begin 
         result := true; 
         exit; 
      end; 
      if timer - t > timeout then // провал по таймауту 
      begin 
         AddToSystemJournal('Move: Time moved out!'); 
         result := false; 
         exit; 
      end; 
      Dir := SetDirection(x, y); 
      CalcPrognosis(Dir); 
      if WrongMove(PrognosisX, PrognosisY) then 
         begin 
         for i := 1 to 7 do 
            begin 
            Dir := RewindDir(Dir, 1); 
            CalcPrognosis(Dir); 
            if not WrongMove(PrognosisX, PrognosisY) then break; 
            end; 
      if i = 8 then 
         begin 
         AddToSystemJournal('Move: Cannot move'); 
         result := false; 
         exit; 
         end; 
      end; 
     // timeout := timeout + CheckFlow; 
      lastX := GetX(self); lastY := GetY(self); 
      if TryToMove(Dir, RunFlag) then SetWrongMove(lastX, lastY) // отсюда чар пришёл 
      else SetWrongMove(PrognosisX, PrognosisY); 
      end; 
end; 

procedure _move(x, y, tolerance : integer; RunFlag : boolean); 
begin 
while not Move(x, y, tolerance, RunFlag) do wait(100); 
end;
PS: Кстати, здесь реализован алгоритм обычной трассировки, а не надёжной, как написал Somebody.
Alexej
Novice
Novice
Posts: 79
Joined: 06.06.2008 11:55
Contact:

Post by Alexej »

данная ходилка подходит для перемещаения по открытой местности с препятствиями (типа лес и тд), но в шахтах, она почему то тупит и ищет пути прохода через стены шахты :) вообщем в результате получаем что чар вместо нужной точки уходит кудато еще дальше, потом заворачивает за любой угол и пытается пройти через стену... ну тупит вообщем, почему - непонятно, я неразгадал сиего чуда...
Antoska
Neophyte
Neophyte
Posts: 45
Joined: 10.07.2009 22:21
Location: Латвия, Рига

Post by Antoska »

Ага, у меня в пещере тоже тупить начинает.
grundick
Developer
Developer
Posts: 272
Joined: 31.01.2008 21:16

Post by grundick »

Алгоритм изучите и поймёте в чём проблема ). Он совсем простой для понимания.
Antoska
Neophyte
Neophyte
Posts: 45
Joined: 10.07.2009 22:21
Location: Латвия, Рига

Post by Antoska »

Не, для меня это слишком тяжко...)
Post Reply