Исполнение скриптов, написаных к L2Walker, из клиента игры
Довольно много готовых скриптов для валкера. Может стоит воспользоваться? Тем более, что для WP PPC есть подобное.
По крайней мере можно будет пользоваться в скриптах для пакетхака.
Руководство пользователя:
Открываем скрипт в Блокноте и сохраняем его в ANSI кодировке в папку scripts, которая расположена в папке с пакетхаком.
В ниже приведённом скрипте изменяем в таких строчках, указанные там значения на нужные нам:
delphi Код:
Name='NLObP'; //имя персонажа в игре
PathWalkerScript='.\scripts\'; //путь к скрипту
NameWalkerScript='script.sec'; //имя скрипта
что он может:
1.исполнение скриптов, написаных к L2Walker, из клиента игры с помощью L2PacketHack
version 0.11 от 03.06.2009г.
[+] Понимает команды:
LoadItem(ITEMNAME[ID=#],#)
SaveItem(ITEMNAME[ID=#],#)
[+] Ведём базу хранилища[*] Подправил GetBypass, теперь можно писать в команде DLGSEL(), только начало строки. [*] Подправил команду DLGSEL() для того, чтобы она ждала пока чар не подбежит на достаточное расстояние к НПЦ
version 0.10 от 30.04.2009г.
[+] Команды управления из чата START|RUN, STOP, PAUSE, RESUME, загрузка скрипта SCRIPT|LOAD=TEST
[+] Добавил управление ботом - Алексус, который пришлось модифицировать для поодержки данного скрипта
[+] Добавил POSOUTRANGE, POSINRANGE
[+] CharStatus([CHP|CMP|HP|MP|WEIGHT|LV|SP|RACE|STAND],[>=|>|==|!=|<|<=],число)
[+] Добавил UserInfo, StatusUpdate
[+] Добавил SET(MON,ATTACK|NOATTACK|ATTACKONE,Name[ID=n];Name1[ID=n];|*), SET(MON,NOATTACK,*)
[+] Добавил SET(RangeType,DefPos,x,y,z,radii), SET(RangeType,StartPos,radii)[*] мелкие правки кода[*] вложенные условия[*] поменял логику применения RequestEnchantItem
version 0.9 от 02.02.2009г.[*] Поправил ITEMCOUNT
version 0.8 от 02.02.2009г.
[+] Понимает команды:
CrystallizeItem(Name1[Id=XXXX],Count;Name2[Id=XXXX],Count)
version 0.7 от 20.01.2009г.
[+] Понимает команды:
MSG(сообщение),
SELLITEM(Name1[Id=XXXX],Count) или SELLITEM(Name1[Id=XXXX],Count;Name2[Id=XXXX],Count),
DLGSEL(теперь и кнопки жмёт)
version 0.6 от 19.01.2009г.
[+] Понимает команды:
BUYITEM(Name1[Id=XXXX],Count) или BUYITEM(Name1[Id=XXXX],Count;Name2[Id=XXXX],Count)
version 0.5 от 18.01.2009г.
[+] Понимает команды:
PAUSE(время в мс),
ITEMCOUNT(Name[ID=XXXX],==|=>|<=|!=|<>,Count)
version 0.4 от 18.01.2009г.
[+] Понимает команды:
MOVETO(x,y,z),
USEITEM(Name[Id=XXX]),
CALL(метка),
RETURN
version 0.3 от 17.01.2009г.
[+] Понимает команды:
NPCDLG(Name[Id=XXX]),
NPCSEL(Name[Id=XXX])
version 0.2 от 16.01.2009г.
[+] Понимает команды:
DELAY(время в мс),
JMP(метка),
LABEL(имя метки),
EXIT,
DLGSEL(только текст, кнопки не жмёт)
[+] Ведём базу инвентаря
[+] Ведём базу NPCs
Выкладываю новую версию скрипта, которая совместно с ботом - локомотивом эмулирует валкера (для сервера Абисс). Выполняемые команды:
[+] Команды управления из чата START|RUN, STOP, PAUSE, RESUME, загрузка скрипта SCRIPT|LOAD=TEST
[+] Добавил управление ботом - Алексус, который пришлось модифицировать для поодержки данного скрипта
[+] Добавил POSOUTRANGE, POSINRANGE
[+] CharStatus([CHP|CMP|HP|MP|WEIGHT|LV|SP|RACE|STAND],[>=|>|==|!=|<|<=],число)
[+] Добавил UserInfo, StatusUpdate
[+] Добавил SET(MON,ATTACK|NOATTACK|ATTACKONE,Name[ID=n];Name1[ID=n];|*), SET(MON,NOATTACK,*)
[+] Добавил SET(RangeType,DefPos,x,y,z,radii), SET(RangeType,StartPos,radii)
[*] мелкие правки кода
[*] вложенные условия
[*] поменял логику применения RequestEnchantItem
//******************************************************************************program WalkerScriptRunner;
//******************************************************************************{version 0.10 от 30.04.2009г.
Walker Script Runner by NLObP for Russian Official Server Linage 2 (Gracia)
Эмулятор Walker'а: возможность запуска скриптов валкера в L2PacketHack 3.4.1+ by CoderX.ru
спасибо Trevor from allcheats.ru
_http://allcheats.ru/t37228/
что он может:
1.исполнение скриптов, написаных к L2Walker, из клиента игры
ВНИМАНИЕ:
Скрипт переводим в Ansi кодировку!
Настроим скрипт, найдя ниже по тексту такие строки и вставив тута нужные значения
Name='NLObP'; //имя персонажа в игре
NameWalkerScript='cristallize.sec'; //имя скрипта}//При написани скриптов соблюдайте некоторые правила://1. команды пишутся в столбик//2. если нужно написать команду с {}, то делается так//CharStatus(***,***,***)//{//КОМАНДЫ//}//Или так//CharStatus(***,***,***)//{КОМАНДЫ}//Или так//CharStatus(***,***,***)//{//CharStatus(***,***,***)//{//КОМАНДЫ//}//}//******************************************************************************const
Name='RemoteAccess'; //имя персонажа в игре//Name='NLObP'; //имя персонажа в игре
PathWalkerScript='.\scripts\walkerscript\'; //путь к скрипту
NameWalkerScript='default.sec'; //имя скрипта загружаемого по умолчанию
DefaultExecuteDelay=100; //стандартная задержка между коммандами Валкера
DefaultDistanciya=200; //дистанция при которой считаем, что пришли в нужную точку
debug=false; //если не хотим видеть отладочной информации - FALSE, иначе - TRUE
maxitems=250; //max количество предметов в базе
maxnpc=500; //max количество контролируемых NPC
kID=1000000;
//..............................................................................var
WalkerScript, //сюда загружаем скрипт валкера
WalkerLabel, //здесь храним адреса меток в скрипте
ReturnStack: TStringList; //здесь храним адреса возврата по call()
sHTML, //здесь хранится текст сообщения из пакета NpcHtmlMessage
bypass: string; //для записи байпаса выбранной строчки в меню
TargetIDobject: integer;
ExecuteTimer: Ttimer; //основной таймер исполнения команд
ExecuteDelay: integer; //задержка между командами валкера
MoveTimer: Ttimer; //таймер движения
MoveDelay: integer; //задержка между командами движения
chkDelta: integer; //сохраняем дельту для проверки застревания
chkDelta2: integer; //сохраняем дельту для проверки застревания
strIndex: integer; //номер исполняемой строки в скрипте валкера
MyRace, MySex, //характеристики нашего чара
MyClass, MyLevel : integer;
MyOID, MyX, MyY, MyZ, MyMAX_HP, MyCUR_HP, MyMAX_MP,
MyCUR_MP, MyCur_CP, MyMax_CP, MySP, MyCUR_LOAD, MyMAX_LOAD : longint;
MyName:string;
X1, Y1, Z1, Delta1: integer;//координаты из команды MOVETO()
Running: boolean; //для вычисления скорости передвижения чара
runSpd, walkSpd : integer;
speed : extended;
//требуется поддержка БД//BuyList
BaseBuyItems: array[1..maxitems, 1..2] ofinteger; //все предметы в инвентаре продавца
BuyListID: integer; //ID списка на продажу (всегда новый)//Chars OID=Name...//NPCs
BaseNPCs: array[1..maxnpc, 1..3] ofinteger; //все NPC вокруг
NpcCount : integer; //количество Npc в базе{
1 - OID
2 - ID
3 - Attacable //можно атаковать - 1 или нет - 0
}//ITEMs
BaseItems: array[1..maxitems, 1..12] ofinteger; //все предметы в инвентаре//******************************************************************************procedure Init; //Вызывается при включении скриптаvar
i, j :integer;
begin
debugMsg('WSR запущен!');
WalkerScript:=TStringList.Create;
WalkerLabel:=TStringList.Create;
ReturnStack:=TStringList.Create;
//загружаем скрипт валкера из файла
WalkerScript.LoadFromFile(PathWalkerScript+NameWalkerScript);
MakeLabel; //готовим адреса меток из скрипта Валкера//чистим базыfor i:=1to maxitems dobeginfor j:=1to12do BaseItems[i,j]:=0; // забиваем нулямиend;
for i:=1to maxnpc dobeginfor j:=1to3do BaseNPCs[i,j]:=0; // забиваем нулямиend;
//14=RequestItemList вызываем инвентарь
buf:=hstr('14');
SendToServerEx(Name);
//delay(250);//by Xelat{я видел у тебя там проблемы с пакетом UserInfo - отправляешь на сервер пакет 6E
- RequestRecordInfo и тебе придёт и юзеринфо, и инфа о всех нпц и игроках,
которые тут есть}
buf:=#$6E;
SendToServerEx(Name);
//delay(250);
strIndex:=0; //начинаем с первой строки
ExecuteDelay:=DefaultExecuteDelay; //задержка между командами валкера
ExecuteTimer:=TTimer.Create(nil);
ExecuteTimer.Enabled:=false; //по умалчанию ExecuteTimer.Enabled:=false;
ExecuteTimer.Interval:=ExecuteDelay; //время задержки
ExecuteTimer.OnTimer:=@OnExecute;
MoveTimer:=TTimer.Create(nil);
MoveTimer.Enabled:=False;
MoveTimer.Interval:=1500; //время задержки
MoveTimer.OnTimer:=@OnMove;
end;
//..............................................................................procedure Free; //Вызывается при выключении скриптаbegin
ExecuteTimer.Enabled:=False; //остановим на всякий случай
Executetimer.Free;
MoveTimer.Enabled:=False; //остановим на всякий случай
movetimer.Free;
WalkerScript.Free;
WalkerLabel.Free;
ReturnStack.Free;
end;
//******************************************************************************{
Вспомогательные процедуры и функции
}//******************************************************************************//..............................................................................function RequestEnchantItem(sData: string): boolean;
{
sData = Название предмета[ID=#];Название предмета[ID=#]
Tип: 0x19 (UseItem)
Pазмер: 9+2
Время прихода: 17:15:28:750
0002 d ObjectID: 268482417 (0x1000B771)
0006 d Unknown: 0 (0x00000000)
Tип: 0x5F (RequestEnchantItem)
Pазмер: 9+2
Время прихода: 17:15:34:015
0002 d ObjectID: 268482423 (0x1000B777)
отменили enchant
Tип: 0x5F (RequestEnchantItem)
Pазмер: 9+2
Время прихода: 17:37:27:421
0002 d ObjectID: -1 (0xFFFFFFFF)
результат: успешно
Tип: 0x87 (EnchantResult)
Pазмер: 13+2
Время прихода: 17:51:26:109
0002 d Result: 0 (0x00000000)
результат:разбили на кристаллы
Tип: 0x87 (EnchantResult)
Pазмер: 13+2
Время прихода: 17:53:29:703
0002 d Result: 1 (0x00000001)
результат:сбросило в 0 для блессед скроллов
Tип: 0x87 (EnchantResult)
Pазмер: 13+2
Время прихода: 17:53:29:703
0002 d Result: 2 (0x00000002)
}var
i, ListSize, id, count: integer;
s: string;
begin
s:=sData+';'; //чтобы можно было взять число в конце//Название предмета[ID=7807],10;Название предмета[ID=6529],1;
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символаif s<>''thenbegin
result:=false;
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили ID
sendmsg('ID='+inttostr(id));
for i:=1to maxitems dobeginif(BaseItems[i,3]=ID)thenbegin//ищем по ItemID
debugMsg('UseItem');
buf:=#$19;
WriteD(BaseItems[i,2]); //пишем в пакет ObjectID
WriteD(0);
SendToServerEx(Name);
break;
end;
end;
delay(500);
//Название предмета[ID=7807],10;Название предмета[ID=6529],1;
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символаif s<>''thenbegin
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили ID
sendmsg('ID='+inttostr(id));
for i:=1to maxitems dobeginif(BaseItems[i,3]=ID)thenbegin//ищем по ItemID
debugMsg('EnchantItem');
buf:=#$5F;
WriteD(BaseItems[i,2]); //пишем в пакет ObjectID
SendToServerEx(Name);
result:=true; //вещь, которую надо точить нашласьbreak;
end;
end;
endelsebegin//отменили энчант
debugMsg('отмена EnchantItem');
buf:=#$5F;
WriteD(-1); //пишем в пакет об отмене заточки
SendToServerEx(Name);
result:=false; //вещь, которую надо точить не нашласьend;
end;
end;
//..............................................................................procedure RequestCrystallizeItem(sData: string);
{CrystallizeItem(Name1[Id=XXX],Count;Name2[Id=XXX],Count)
sData = Название предмета[ID=#],Count;Название предмета[ID=#],Count
или
sData = Название предмета[ID=#],Count
Tип: 0x2F (RequestCrystallizeItem)
Pазмер: 9+2
Время прихода: 23:27:03:265
0002 d ObjectID: 268482418 (0x1000B772)
0006 d Count: 1 (0x00000001)
}var
i, id, count: integer;
s: string;
eos: boolean;
begin
debugMsg('RequestCristallizeItem');
s:=sData+';'; //чтобы можно было взять число в конце
Count:=0;
eos:=false;
whilenot eos dobegin//Название предмета[ID=7807],10;Название предмета[ID=6529],1;
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символаif s<>''thenbegin
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили ID
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
count:=strtoint(RTrimEx(ExtractName(s, ';'), ';')); //получили условиеfor i:=1to maxitems dobeginif(BaseItems[i,3]=ID)thenbegin//ищем по ItemID
buf:=#$2F; //начнем писать пакет
WriteD(BaseItems[i,2]); //пишем в пакет ObjectIDif count<=BaseItems[i,5] then WriteD(Count)else WriteD(BaseItems[i,5]);//пишем в пакет count
SendToServerEx(Name); //отсылаем пакет//может сделать паузу вместо break и пройтись по всему инвентарю?
delay(500);
break;
end;
end;
endelse eos:=true; //сигналим, что закончили поискend;
end;
//..............................................................................procedure BuyList;
{Создаем базу BaseBuyItems
0002 d Money: 1140737 (0x00116801)
0006 d ListID: 3157300 (0x00302D34)
0010 h ListSize: 28 (0x001C)
[Начало повторяющегося блока 1/28]
0012 h ItemType1: 4
0014 d 0: 0
0018 d ItemID: Зеленая Наживка (для Новичков) ID:7807 (0x1E7F)
0022 d CurrentCount: 0
0026 h ItemType2: 5
0028 h 0: 0
0030 d BodyPart: 0
0034 h 0: 0
0036 h 0: 0
0038 h 0: 0
0040 d Price*TaxRate: 75
0044 d 0: 0
0048 d 0: 0
0052 d 0: 0
0056 d 0: 0
0060 d 0: 0
0064 d 0: 0
0068 d 0: 0
0072 d 0: 0
[Конец повторяющегося блока 1/28]
[Начало повторяющегося блока 2/28]
0076 h ItemType1: 4
}var
i, j, k: integer;
ListCount: integer;
begin
j:=6; //смещение для ListID
BuyListID:=ReadD(j);
ListCount:=ReadH(j); //количество итемов не должно превышать max!
debugmsg('BuyListID='+inttostr(BuyListID)+' ListCount='+inttostr(ListCount));
for i:=1to maxitems dobeginif(i<=ListCount)thenbegininc(j,6);
BaseBuyItems[i,1]:=ReadD(j); //ItemIDinc(j,18);
BaseBuyItems[i,2]:=ReadD(j); //Price*TaxRateinc(j,32);
//debugmsg('ID='+inttostr(BaseBuyItems[i,1])+' price='+inttostr(BaseBuyItems[i,2]));endelsefor k:=1to2do BaseBuyItems[i,k]:=0; // забиваем нулямиend;
end;
//..............................................................................function RequestBuyItem(sData: string): boolean;
{
BuyItem(Название предмета[ID=#],#;Название предмета[ID=#],#)
sData = Название предмета[ID=#],#;Название предмета[ID=#],#
}var
i, ListSize, id, count: integer;
s: string;
eos: boolean;
begin
debugMsg('RequestBuyItem');
s:=sData+';'; //чтобы можно было взять число в конце
ListSize:=0;
eos:=false;
{
0002 d ListID: 3157300 (0x00302D34)
0006 d ListSize: 2 (0x00000002)
[Начало повторяющегося блока 1/2]
0010 d ItemID: Зеленая Наживка (для Новичков) ID:7807 (0x1E7F)
0014 d Count: 10
[Конец повторяющегося блока 1/2]
[Начало повторяющегося блока 2/2]
0018 d ItemID: Удочка Утки ID:6529 (0x1981)
0022 d Count: 1
[Конец повторяющегося блока 2/2]
}
buf:=#$40;
WriteD(BuyListID);
WriteD(00); //ListCount - забили местоwhilenot eos dobegin//Название предмета[ID=7807],10;Название предмета[ID=6529],1;
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символаif s<>''thenbegin
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили ID//sendmsg('ID='+inttostr(id));
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
count:=strtoint(RTrimEx(ExtractName(s, ';'), ';')); //получили условие//sendmsg('Count='+inttostr(count));for i:=1to maxitems dobeginif(BaseBuyItems[i,1]=ID)thenbegin
WriteD(ID); //пишем в пакет ItemID
WriteD(Count);
inc(ListSize); //увеличиваем кол-во записей в пакете закупки//sendmsg('ID='+inttostr(id)+' count='+inttostr(count)+' ListSize='+inttostr(listsize));break;
end;
end;
endelse eos:=true; //сигналим, что закончили поискend;
if ListSize>0thenbegin//sendMsg('пишем ListSize');
WriteD(ListSize,6); //изменяем начиная с позиции 6//sendMsg('шлём пакет RequestBuyItem='+strtohex(buf));
SendToServerEx(Name);
end;
end;
//..............................................................................function RequestSellItem(sData: string): boolean;
{
SellItem(Название предмета[ID=#],#;Название предмета[ID=#],#)
sData = Название предмета[ID=#],#;Название предмета[ID=#],#
}var
i, ListSize, id, count: integer;
s: string;
eos: boolean;
begin
debugMsg('RequestSellItem');
s:=sData+';'; //чтобы можно было взять число в конце
ListSize:=0;
eos:=false;
{
0x37 (RequestSellItem)
0002 d ListID: 0 (0x00000000)
0006 d ListSize: 2 (0x00000002)
[Начало повторяющегося блока 1/2]
0010 d ObjectID: 268482465
0014 d ItemID: Зеленая Наживка (для Новичков) ID:7807 (0x1E7F)
0018 d Count: 2
[Конец повторяющегося блока 1/2]
[Начало повторяющегося блока 2/2]
0022 d ObjectID: 268481851
0026 d ItemID: Зелье Исцеления ID:1061 (0x0425)
0030 d Count: 1
[Конец повторяющегося блока 2/2]
}
buf:=#$37;
WriteD(0); //SellListID = 0?
WriteD(0); //ListCount - забили местоwhilenot eos dobegin//Название предмета[ID=7807],10;Название предмета[ID=6529],1;
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символаif s<>''thenbegin
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили ID//sendmsg('ID='+inttostr(id));
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
count:=strtoint(RTrimEx(ExtractName(s, ';'), ';')); //получили условие//sendmsg('Count='+inttostr(count));for i:=1to maxitems dobeginif(BaseItems[i,3]=ID)thenbegin//ищем по ItemID
WriteD(BaseItems[i,2]); //пишем в пакет ObjectID
WriteD(BaseItems[i,3]); //пишем в пакет ItemIDif count<=BaseItems[i,5] then WriteD(Count)else WriteD(BaseItems[i,5]);//пишем в пакет countinc(ListSize); //увеличиваем кол-во записей в пакете закупки//sendmsg('ID='+inttostr(id)+' count='+inttostr(count)+' ListSize='+inttostr(listsize));break;
end;
end;
endelse eos:=true; //сигналим, что закончили поискend;
if ListSize>0thenbegin//sendMsg('пишем ListSize');
WriteD(ListSize,6); //изменяем начиная с позиции 6//sendMsg('шлём пакет RequestBuyItem='+strtohex(buf));
SendToServerEx(Name);
end;
end;
//..............................................................................procedure ItemList;
{Создаем базу Items}//11=ItemList:h(ShowWindow)h(ListSize:Loop.01.0021)h(ItemType1)d(ObjectID)// d(ItemID:Get.Func01)d(LocationSlot)d(Count)h(ItemType2)h(CustomType1)// h(isEquipped)d(BodyPart)h(EnchantLevel)h(CustType2)d(AugmentationID)// d(Mana)d(AttackAttrElement)d(AttackAttrElementVal)d(DefAttrFire)// d(DefAttrWater)d(DefAttrWind)d(DefAttrEarth)d(DefAttrHoly)d(DefAttrUnholy)var
i, j, k: integer;
ListCount: integer;
begin
j:=4; //смещение для ListCount
ListCount:=ReadH(j); //количество итемов не должно превышать max!for i:=1to maxitems dobeginif(i<=ListCount)thenbegin
BaseItems[i,1]:=ReadH(j); //ItemType1
BaseItems[i,2]:=ReadD(j); //ObjectID
BaseItems[i,3]:=ReadD(j); //ItemID
BaseItems[i,4]:=ReadD(j); //LocationSlot
BaseItems[i,5]:=ReadD(j); //Count
BaseItems[i,6]:=ReadH(j); //ItemType2
BaseItems[i,7]:=ReadH(j); //CustomType1
BaseItems[i,8]:=ReadH(j); //isEquipped
BaseItems[i,9]:=ReadD(j); //BodyPart
BaseItems[i,10]:=ReadH(j); //EnchantLevel
BaseItems[i,11]:=ReadH(j); //CustType2
BaseItems[i,12]:=ReadD(j); //AugmentationID//debugmsg('OID='+inttostr(BaseItems[i,2])+' ID='+inttostr(BaseItems[i,3]));inc(j,40);
endelsefor k:=1to12do BaseItems[i,k]:=0; // забиваем нулямиend;
end;
//..............................................................................procedure InventoryUpdate; //пакет 21, Создает базу ObjectID по ItemIDvar
i, ii, j, k, ListCount, UpdType : integer;
ItemType1, ObjectID, ItemID, LocationSlot, Count, ItemType2, CustomType1,
isEquipped, BodyPart, EnchantLevel, CustType2, AugmentationID :integer;
additem : boolean;
begin
additem:=true;
ListCount:=ReadH(2); //количество итемов//debugmsg('ListCount='+inttostr(ListCount));
j:=4; //смещение для действия с предметом 1-добавлен 2-изменен 3-удаленfor i:=1to ListCount dobegin
UpdType:=ReadH(j);
ItemType1:=ReadH(j);
ObjectID:=ReadD(j);
ItemID:=ReadD(j);
LocationSlot:=ReadD(j);
Count:=ReadD(j);
ItemType2:=ReadH(j);
CustomType1:=ReadH(j);
isEquipped:=ReadH(j);
BodyPart:=ReadD(j);
EnchantLevel:=ReadH(j);
CustType2:=ReadH(j);
AugmentationID:=ReadD(j);
case UpdType of1: k:=0; //добавлен новый предмет2: k:=ObjectID; //изменен предмет в инвентаре3: begin//удаленfor ii:=1to maxitems doif(BaseItems[ii,2]=ObjectID)thenbegin//debugmsg('удаляем OID='+inttostr(BaseItems[ii,2])+' ID='+inttostr(BaseItems[ii,3]));for k:=1to12do BaseItems[ii,k]:=0;
break;
end;
end;
end;
for ii:=1to maxitems dobeginif(BaseItems[ii,2]=k)thenbegin
BaseItems[ii,1]:=ItemType1;
//BaseItems[ii,2]:=ObjectID;
BaseItems[ii,3]:=ItemID;
BaseItems[ii,4]:=LocationSlot;
BaseItems[ii,5]:=Count;
BaseItems[ii,6]:=ItemType2;
BaseItems[ii,7]:=CustomType1;
BaseItems[ii,8]:=isEquipped;
BaseItems[ii,9]:=BodyPart;
BaseItems[ii,10]:=EnchantLevel;
BaseItems[ii,11]:=CustType2;
BaseItems[ii,12]:=AugmentationID;
//debugmsg('доб/изм OID='+inttostr(BaseItems[ii,2])+' ID='+inttostr(BaseItems[ii,3]));break;
end;
end;
inc(j,38);
end;
end;
//..............................................................................function GetItem(ID, FieldIn, FieldOut : integer): integer;
{GetInv(по чему будем искать, номер того по чему будем искать, номер того что надо найти)
где:
ID - искомый код;
FieldIn - по какому полю ищем;
FieldOut - какое поле возвращаем;
1=ItemType1; 2=ObjectID;3=ItemID;4=LocationSlot;5=Count;6=ItemType2;7=CustomType1;
8=isEquipped;9=BodyPart;10=EnchantLevel;11=CustType2;12=AugmentationID;
ex1: GetInv(6408,2,1) - вернет ObjectID свадебного платья, если онное лежит в
инвентаре, иначе вернет -1
ex2: GetInv(6408,2,8) - вернет уровень заточки первого попавшегося в инвентаре
свадебного платья, если свадебного платья нет, то вернет -1
}var
i: integer;
beginfor i:=1to maxitems doif(BaseItems[i,FieldIn]=ID)thenbegin
Result:=BaseItems[i,FieldOut];
debugmsg('Нашли='+inttostr(result));
exit;
end;
Result:=-1;
end;
//..............................................................................procedure AppendNpc(OID, ID, Att : integer);
{добавляем в базу данных NPC и мобов}var
i: integer;
isExists:boolean;
begin
isExists:=false;
for i:=1to maxnpc dobegin//если нашли в базе, то коректируемif(BaseNPCs[i,1]=OID)and(MyOID<>OID)thenbegin
BaseNPCs[i,2]:=ID; //ID
BaseNPCs[i,3]:=Att; //Attacable
isExists:=true;
end;
end;
ifnot isExists thenbegin//иначе, перебираем базу, ищем свободную ячейку в ней и добавляем новогоfor i:=1to maxnpc dobeginif(BaseNPCs[i,1]=0)and(MyOID<>OID)thenbegin//запоминаем моба в свободную ячейку
BaseNPCs[i,1]:=OID; //OID
BaseNPCs[i,2]:=ID; //ID
BaseNPCs[i,3]:=Att; //Attacablebreak;
end;
end;
end;
end;
//..............................................................................procedure DeleteNpc(OID : integer);
{удаляем Npc}var
i : integer;
beginfor i:=1to maxnpc dobegin//если нашли в базе, то удаляем егоif(BaseNPCs[i,1]=OID)thenbegin//debugmsg('Удаляем '+inttostr(BaseNPCs[i,1])+' '+inttostr(BaseNPCs[i,2]));
BaseNPCs[i,1]:=0; //OID
BaseNPCs[i,2]:=0; //ID
BaseNPCs[i,3]:=0; //IDbreak;
end;
end;
end;
//..............................................................................function GetNpcOID(ID:integer): integer;
{ищем в БД OID по ID}var
i: integer;
beginfor i:=1to maxnpc doif(BaseNpcs[i,2]=ID)thenbegin
Result:=BaseNpcs[i,1];
exit;
end;
Result:=-1;
end;
//..............................................................................function RtrimEx(sData, sDelimiter: string): string;
{Удаление из строки S заданные символы справа}var
m,i : integer;
s: string;
begin
s:=sData;
i:=0;
while i=0dobegin
m:=length(s);
if m>0thenbeginif s[m]<>sDelimiter then i:=1;
if s[m]=sDelimiter thendelete(s,m,1);
end;
if m <= 0then i:=1;
end;
result:=s;
end;
//..............................................................................function LtrimEx(sData, sDelimiter:String): string;
{Удаление из строки S заданные символы слева}var
m,i : integer;
s: string;
begin
s:=sData;
i:=0;
while i=0dobegin
m := length(s);
if m > 0thenbeginif s[1]<>sDelimiter then i:=1;
if s[1]=sDelimiter thendelete(s,1,1);
end;
if m <= 0then i:=1;
end;
result:=s;
end;
//..............................................................................function Ltrim(sData:String): string;
{Удаление из строки S заданные символы слева}begin
result:=LtrimEx(sData,' ');
end;
//..............................................................................function Rtrim(sData:String): string;
{Удаление из строки S заданные символы слева}begin
result:=RtrimEx(sData,' ');
end;
//..............................................................................function AllTrimEx(sData, sDelimiterLeft, sDelimiterRight: String): string;
{Удаление из строки S заданные символы слева и справа}begin
result:=LtrimEx(RtrimEx(sData, sDelimiterRight), sDelimiterLeft);
end;
//..............................................................................function AllTrim(sData: String): string;
{Удаление из строки S заданные символы слева и справа}begin
result:=Ltrim(Rtrim(sData));
end;
//..............................................................................{
В TStringList и Tstring -> Name=Value
123=ghjdthrf
i:=WalkerScript.IndexOfName(IntToHex(id,2));
i:=WalkerScript.Values[123]; //по value возвращает Name
i:=WalkerScript.Names[123]; //по Name возвращает Value
}function GetValues(ValName: string): string;
begin
result:=WalkerScript.Values[ValName];
end;
//..............................................................................function GetNames(Value: string): string;
begin
result:=WalkerScript.Names[strtoint(Value)];
end;
//..............................................................................procedure SetValues(ValName: string; Value: string);
begin
WalkerScript.Values[ValName]:=Value;
end;
//..............................................................................procedure SetNames(Value: string; Name: string);
begin
WalkerScript.Names[strtoint(Value)];
end;
//..............................................................................function ExtractValue(sData, sFind: string;): string;
{возвращаем конец строки после найденного символа}var
s: string;
i,j: integer;
begin
i:=0;
result:='';
i:=find(sData, sFind);
if i>0then result:=copy(sData, i+length(sFind), length(sData));
end;
//..............................................................................function ExtractName(sData, sFind: string): string;
{возвращаем строку до найденного символа}var
i: integer;
begin
i:=0;
result:='';
i:=find(sData, sFind);
//if i>0 then result:=copy(sData,1,i-length(sFind));if i>0then result:=copy(sData, 1, i-length(sFind)+1);
end;
//..............................................................................function Find(const S, P: string): Integer;
{Функция Find ищет подстроку P в строке S и возвращает индекс первого символа
подстроки или 0, если подстрока не найдена. Хотя в общем случае этот метод,
как и большинство методов грубой силы, малоэффективен, в некоторых ситуациях
он вполне приемлем.}var
i, j: Integer;
begin
Result:=0;
ifLength(P)>Length(S)thenbegin
debugMSG('Несоответствие длин: p='+inttostr(Length(P))+' > S='+inttostr(Length(s)));
debugMSG('Строка: '+inttostr(strIndex));
Exit;
end;
for i:=1toLength(S)-Length(P)+1do//x0 начало смещения для поиска в строкеbeginfor j:=1toLength(P)dobeginif P[j]<>S[i+j-1] thenBreakelseif j=Length(P)thenbegin
Result:=i;
Exit;
end;
end;
end;
end;
//..............................................................................function GetID(sData: string): string;
{находим ID в строке sData
NPCSEL(Marcela[ID=32173])
NPCDLG(Marcela[ID=32173])
}var
i: integer;
s, sID: string;
eos: boolean; //конец NpcHtmlMessagebegin
debugMsg('GetID');
s:=sData;
result:='';
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символа//debugMsg('ExtractValue:'+s);if s=''thenbegin//debugMsg('BREAK');exit;
end;
sID:=RTrimEx(ExtractName(s, ']'), ']'); //получили ID//debugMsg('ExtractName:'+sID);
result:=sID;
end;
//..............................................................................function GetBypass(sData, sText: string): string;
{находим в NpcHtmlMessage по заданной строке текста соответствующий ей bypass
Например:
Gatekeeper Clarissa:
<br> We Gatekeepers use the will of the Gods to open the doors to time and space and teleport others. Which door would you like to open?<br>
<a action="bypass -h teleport_request"> Teleport</a><br>
<a action="bypass -h menu_select?ask=-303&reply=518"> Exchange with the Dimension Diamond</a><br>
<a action="bypass -h menu_select?ask=-19&reply=0"> [Noblesse Only] teleport</a><br>
<a action="bypass -h menu_select?ask=255&reply=4" msg="811;Monster Derby Track"> Move to Monster Derby Track (Free of Charge)</a><br>
<a action="bypass -h talk_select">Quest </a></body></html>
}var
i: integer;
s, bypass, text: string;
eos: boolean; //конец NpcHtmlMessagebegin
debugMsg('GetBypass');
s:=sData;
eos:=false;
result:='';
//поискать bypass в строкеwhilenot eos dobegin//пока не конец NpcHtmlMessage считываем данные
s:=ExtractValue(s,'-h'); //получили остаток строки начиная с искомого символа//debugMsg('ExtractValue:'+s);if s=''thenbegin//debugMsg('BREAK');break; //eos:=true; //сигналим, что закончили поискend;
bypass:=AllTrim(ExtractName(s, '">')); //получили bypass//debugMsg('ExtractName:'+bypass);
s:=ExtractValue(s,'">'); //получили остаток строки начиная с искомого символа//debugMsg('ExtractValue: '+s);text:=AllTrim(ExtractName(s, '</')); //получили текст//debugMsg('ExtractName:'+text);if sText=textthenbegin//debugMsg('Нашли подходящий bypass: '+bypass);
result:=bypass; //нашли подходящий bypass
eos:=true; //сигналим, что закончили поискend;
end;
//debugMsg('не нашли в строке');if result=''thenbegin//поискать bypass в кнопке//debugMsg('поищем в кнопке');
s:=sData;
eos:=false;
whilenot eos dobegin//пока не конец NpcHtmlMessage считываем данные
s:=ExtractValue(s,'button value="'); //получили остаток строки начиная с искомого символа//debugMsg('ExtractValue:'+s);if s=''thenbegin//debugMsg('BREAK');break; //сигналим, что закончили поискend;
text:=AllTrim(ExtractName(s, '"')); //получили текстtext:=RTrimEx(text,'"'); //получили текст
s:=ExtractValue(s,'-h'); //получили остаток строки начиная с искомого символа//debugMsg('ExtractValue: '+s);
bypass:=AllTrim(ExtractName(s, '"')); //получили bypass
bypass:=RTrimEx(bypass,'"'); //получили текст//debugMsg('ExtractName:'+bypass);//debugMsg('ExtractName:'+text);if sText=textthenbegin//debugMsg('Нашли подходящий bypass: '+bypass);
result:=bypass; //нашли подходящий bypass
eos:=true; //сигналим, что закончили поискend;
end;
end;
end;
//..............................................................................function CorrectCoord(Coord: integer): integer;
{коррекция значений координат}begin
result:=Coord;
if result > 2147483648then result:=result-4294967296;
end;
//..............................................................................procedure GetXYZ(sData: string);
{
находим X, Y, Z в строке sData
MOVETO(-119839,44516,341)
}var
s: string;
begin
debugMsg('GetXYZ');
s:=sData+','; //-119839,44516,341, чтобы можно было взять Z
x1:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили X//x1:=CorrectCoord(x1);
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
y1:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили Y//y1:=CorrectCoord(y1);
z1:=strtoint(RTrimEx(ExtractValue(s,','), ',')); //получили Z//z1:=CorrectCoord(z1);end;
//..............................................................................procedure GetXYZD(sData: string);
{
находим X, Y, Z, Delta в строке sData
Posinrange(-119839,44516,341,100)
}var
s: string;
begin
debugMsg('GetXYZD');
s:=sData+','; //-119839,44516,341,100, чтобы можно было взять Delta
x1:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили X//x1:=CorrectCoord(x1);
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
y1:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили Y//y1:=CorrectCoord(y1);
z1:=strtoint(RTrimEx(ExtractValue(s,','), ',')); //получили Z//z1:=CorrectCoord(z1);
delta1:=strtoint(RTrimEx(ExtractValue(s,','), ',')); //получили Deltaend;
//..............................................................................function GetItemcount(sData: string): boolean;
//ITEMCOUNT(Red Bone Necklace[ID=7179],==,100)//{//}//в sData = Red Bone Necklace[ID=7179],==,100var
s, uslovie: string;
i, id, count2, count: integer;
begin
debugMsg('GetItemcount');
s:=sData+','; //чтобы можно было взять число в конце
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символа
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили ID
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
uslovie:=RTrimEx(ExtractName(s, ','), ','); //получили условие
count2:=strtoint(RTrimEx(ExtractValue(s,','), ',')); //получили число для проверки
count:=0; //GetItem(id,3,5); //возвращает количество из инвентаря//считаем все подходящие предметыfor i:=1to maxitems dobeginif(BaseItems[i,3]=ID)thenbegin
count:=count+BaseItems[i,5];
end;
end;
debugmsg('Нашли='+inttostr(count));
SendMessage('Нашли='+inttostr(count));
//обрабатываем условия, во всевозможных вариацияхcase uslovie of'==','=' : if count=count2 then result:=true;
'=>','>=': if count>=count2 then result:=true;
'>' : if count>count2 then result:=true;
'!=','<>','><' : if count<>count2 then result:=true;
'<' : if count<count2 then result:=true;
'<=','=<': if count<=count2 then result:=true;
end;
end;
//..............................................................................function GetCharStatus(sData: string): boolean;
//CharStatus([CHP|CMP|HP|MP|WEIGHT|LV|SP|RACE|STAND],[>=|>|==|!=|<|<=],число)//{//}//в sData = HP,<,70var
s, conditiontype, uslovie: string;
i, id, count2, count: integer;
begin
debugMsg('GetCharStatus');
s:=sData+','; //чтобы можно было взять число в конце
conditiontype:=RTrimEx(ExtractName(s, ','), ','); //получили название статуса
debugMsg(conditiontype);
s:=ExtractValue(s,','); //получили остаток строки начиная с искомого символа
uslovie:=RTrimEx(ExtractName(s, ','), ','); //получили условие
debugMsg(uslovie);
count2:=strtoint(RTrimEx(ExtractValue(s,','), ',')); //получили число для проверки
debugMsg(inttostr(count2));
count:=0;
{CharStatus(CONDITIONTYPE,>=|>|==|!=|<|<=,#)
Замеряет состояние персонажа. Прочитайте все, что относится к <Операторам>,
чтобы знать как правильно это функционирует.:
CHP --> Здоровье
CharStatus(CHP,>=|>|==|!=|<|<=,#)
CMP: Мана
CharStatus(CMP,>=|>|==|!=|<|<=,#)
HP: ХП персонажа в процентах
CharStatus(HP,>=|>|==|!=|<|<=,#)
MP: МП персонажа в процентах
CharStatus(MP,>=|>|==|!=|<|<=,#)
SP: SP персонажа
CharStatus(SP,>=|>|==|!=|<|<=,#)
LV: Уровень персонажа
CharStatus(LV,>=|>|==|!=|<|<=,#)
Race: Раса персонажа
CharStatus(Race,>=|>|==|!=|<|<=,#)
(#=0: Human; #=1: Elf; #=2: Dark Elf; #=3: Orc; #=4: Dwarf)
}case conditiontype of'CHP' : count:=mycur_hp; //ХП персонажа'CMP' : count:=mycur_mp; //МП персонажа'HP' : count:=mycur_hp * 100div mymax_hp; //ХП персонажа в процентах'MP' : count:=mycur_mp * 100div mymax_mp; //МП персонажа в процентах'WEIGHT': count:=mycur_load * 100div mymax_load;
'LV' : count:=mylevel;
'SP' : count:=mysp;
'RACE' : count:=myrace;
//'STAND' : count:=mystand;end;
//обрабатываем условия, во всевозможных вариацияхcase uslovie of'==','=' : if count=count2 then result:=true;
'=>','>=': if count>=count2 then result:=true;
'>' : if count>count2 then result:=true;
'!=','<>','><' : if count<>count2 then result:=true;
'<' : if count<count2 then result:=true;
'<=','=<': if count<=count2 then result:=true;
end;
end;
//..............................................................................//пропускаем фигурные скобкиprocedure SkipFS;
var
i, j : integer;
s: string;
begin
debugMsg('Пропускаем фигурные скобки...');
j:=0;
for i:=strIndex to WalkerScript.Count-1dobegin
s:=WalkerScript[i]; //считываем команду из листаcase s of//пропускаем всё до соответствующей фигурной скобки'{': begin
debugmsg('Нашли скобку {... strIndex='+inttostr(strIndex));
inc(j);
end;
'}': begin
debugmsg('Нашли скобку }... strIndex='+inttostr(strIndex));
dec(j);
if j=0thenexit;
end;
end;
inc(strIndex);
end;
end;
//..............................................................................//Проверка находится ли заданная точка в пределах досягаемости.function PosInRange(targetx,targety,targetz,distanciya:integer):boolean;
beginif delta(targetx, targety, MyX, MyY)<=distanciya then result:=trueelse result:=false;
end;
//..............................................................................//Проверка находится ли заданная точка за пределами досягаемости.function PosOutRange(targetx,targety,targetz,distanciya:integer):boolean;
beginif delta(targetx, targety, MyX, MyY)>distanciya then result:=trueelse result:=false;
end;
//..............................................................................function delta(xpos1, ypos1, xpos2, ypos2:extended):integer; //возвращает растоянием между 2 точкамиvar
dx, dy, summa: extended;
begintry
dx:=xpos1-xpos2;
dy:=ypos1-ypos2;
summa:=dx*dx+dy*dy;
if summa=0then result:=0else result:=Round(sqrt(summa));
debugmsg('delta='+inttostr(result));
chkDelta:=result; //сохраним дельту для проверки застреванияexcept
debugmsg('error in delta');
end;
end;
//..............................................................................procedure debugMsg(msg: string);
beginif debug thenbegin
sendMSG(msg);
SendMessage(msg);
end;
end;
//******************************************************************************{
Посылаем пакеты
}//******************************************************************************//послать сообщение в чат//use: SendMessage(msg);procedure SendMessage(msg:string); //отправка системных сообщений клиентуbegin
buf:=#$4A;
WriteD(0);
WriteD(10);
WriteS('');
WriteS(msg);
SendToClientEx(Name);
end;
//..............................................................................//использовать предмет//use: UseItem(oid);procedure UseItem(OID, shift: integer);
begin//c19=UseItem:d(ObjectID)d(Unknown)
buf:=#$19;
WriteD(OID);
WriteD(shift);
SendToServerEx(Name);
end;
//..............................................................................//снять цель//use: TargetCancel;procedure TargetCancel;
begin
buf:=Hstr('48 00 00');
SendToServerEx(Name);
end;
//..............................................................................//взять в цель//use: Action(OID);procedure Target(OID: Integer);
begin//c1F=Action:d(ObjectID)d(OriginX)d(OriginY)d(OriginZ)c(ActionID)
buf:=#$1F;
WriteD(OID);
WriteD(MyX);
WriteD(MyY);
WriteD(MyZ);
WriteC(00);
SendToServerEx(Name);
end;
//..............................................................................//атаковать цель, действует толко после Action(OID)//use: AttackRequest(OID);procedure Attack(OID: Integer);
begin//c01=AttackRequest:d(ObjectID)d(OriginX)d(OriginY)d(OriginZ)c(AttackID)
buf:=#$01;
WriteD(OID);
WriteD(MyX);
WriteD(MyY);
WriteD(MyZ);
WriteC(00);
SendToServerEx(Name);
end;
//..............................................................................//Идти в точку с координатами x,y,z//use: MoveBackwardToLocation(X,Y,Z);procedure MoveBackwardToLocation(TargetX,TargetY,TargetZ: integer);
begin//c0F=MoveBackwardToLocation:d(ToX)d(ToY)d(ToZ)d(OrigX)d(OrigY)d(OrigZ)d(MoveMovement)
buf:=#$0F;
WriteD(TargetX); //куда
WriteD(TargetY);
WriteD(TargetZ);
WriteD(MyX); //откуда
WriteD(MyY);
WriteD(MyZ);
WriteD(1); //используем 1-мышь 0-клавиатура
SendToServerEx(Name);
end;
//..............................................................................//выбор пункта меню//use: RequestBypassToServer:s(Cmd)procedure RequestByPassToServer(cmd: string);
begin
buf:=#$23;
WriteS(cmd);
SendToServerEx(Name);
end;
//..............................................................................//******************************************************************************// Поддержка меток используемых в скрипте Валкера//******************************************************************************procedure MakeLabel;
{перебираем строки скрипта и сохраняет в стринглист адреса меток}var
i: integer;
s, cmd, param: string;
begin//собираем метки
debugMsg('Готовим метки...');
WalkerLabel.Clear;
for i:=0to WalkerScript.Count-1dobegin
s:=WalkerScript[i]; //считываем команду из листа
cmd:=UpperCase(RtrimEx(ExtractName(s, '('), '(')); //выцепляем команду
param:=RtrimEx(ExtractValue(s, '('),')'); //выцепляем параметрcase cmd of{безусловный переход на метку}'LABEL': begin
WalkerLabel.Add(param+'='+inttostr(i));
debugmsg(param+'='+inttostr(i));
end;
{всё остальное игнорируем}end;
end;
//WalkerLabel.SaveToFile(PathWalkerScript+'label.txt');end;
//******************************************************************************// Поддержка организации движения//******************************************************************************function OnMove(Sender: TObject): integer; //CommandList: TStringListbegin
debugMsg('Процесс движения...');
if posinrange(x1,y1,z1,DefaultDistanciya)thenbegin
MoveTimer.Enabled:=false;
ExecuteTimer.Enabled:=True;
debugMsg('Добежали...');
endelsebegin
debugMsg('Ещё не добежали...');
if chkDelta2=chkDelta thenbegin
MoveBackwardToLocation(x1,y1,z1);
debugMsg('Ещё раз бежим в точку...');
endelse
chkDelta2:=chkDelta; //сохраняем дельту для проверки застреванияend;
end;
//***************************************************************{
32=UserInfo:d(X)d(Y)d(Z)d(Heading)d(ObjectID)s(Name)d(Race)d(Sex)d(ClassID:Get.ClassID)d(Level)q(Exp)d(STR)d(DEX)d(CON)d(INT)d(WIT)d(MEN)d(MaxHP)d(CurrentHP)d(MaxMP)d(CurrentMP)d(SP)d(CurrentLoad)d(MaxLoad)d(40)d(Unknown)d(RightEarring)d(LeftEarring)d(Necklace)d(RightRing)d(LeftRing)d(Head)d(RightHand)d(LeftHand)d(Gloves)d(Chest)d(Legs)d(Boots)d(Unknown)d(Unknown)d(Hair)d(Face)d(Unknown)d(Unknown)d(0)d(0)d(0)d(0)d(0)d(0)d(Unknown)d(RightEarring)d(LeftEarring)d(Necklace)d(RightRing)d(LeftRing)d(Head)d(RightHand)d(LeftHand)d(Gloves)d(Chest)d(Legs)d(Boots)d(Unknown)d(Unknown)d(Hair)d(Face)d(Unknown)d(Unknown)d(0)d(0)d(0)d(0)d(0)d(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)d(AugmentationID)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)d(Unknown)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)h(0)d(PAtk)d(AtkSpd)d(PDef)d(Evasion)d(Accuracy)d(CritRate)d(MAtk)d(CastSpd)d(AtkSpd)d(MDef)d(PvpFlag)d(Karma)d(RunSpd)d(WalkSpd)d(SwimRunSpd)d(SwimWalkSpd)d(FlRunSpd)d(FlWalkSpd)d(FlyRunSpd)d(FlyWalkSpd)f(MoveMultiplier)f(AtkSpdMultiplier)f(CollisionRadius)f(CollisionHeight)d(HairStyle)d(HairColor)d(Face)d(AccessLevel)s(Title)d(ClanID)d(ClanCrestID)d(AllyID)d(AllyCrestID)d(Relation)c(MounType)c(PrivateStoreType)c(DwarvenCraft)d(PkKills)d(PvpKills)h(CubicsSize:Loop.01.0001)h(CubicID)c(0)d(AbnormalEffect)c(0)d(ClanPrivileges)h(RecomLeft)h(RecomHave)d(MountNpcID)h(InventoryLimit)d(ClassID:Get.ClassID)d(0)d(MaxCP)d(CurrentCP)c(isMount)c(Team)d(ClanCrestLargeID)c(isNoble)c(isHero)c(isFishing)d(FishX)d(FishY)d(FishZ)d(NameColor)c(isRunning)d(PledgeClass)d(0)d(TitleColor)d(CursedWeapon)d(TranformationID)d(AttackAttrElement)d(AttackAttrElementVal)d(DefAttrFire)d(DefAttrWater)d(DefAttrWind)d(DefAttrEarth)d(DefAttrHoly)d(DefAttrUnholy)d(0)
}procedure UserInfo;
{данные о себе}var
i:integer;
begin
i:=2;
MyX:=ReadD(i);
MyY:=ReadD(i);
MyZ:=ReadD(i);
ReadD(i); //пропускаем heading
MyOID:=ReadD(i);
MyName:=ReadS(i);
MyRace:=ReadD(i);
MySex:=ReadD(i);
MyClass:=ReadD(i);
MyLevel:=ReadD(i);
inc(i,32);
MyMAX_HP:=ReadD(i);
MyCUR_HP:=ReadD(i);
MyMAX_MP:=ReadD(i);
MyCUR_MP:=ReadD(i);
MySP:=ReadD(i);
MyCUR_LOAD:=ReadD(i);
MyMAX_LOAD:=ReadD(i);
{
debugMsg('MyName='+MyName);
debugMsg('MyRace='+inttostr((MyRace)));
debugMsg('MySex='+inttostr((MySex)));
debugMsg('MyClass='+inttostr((MyClass)));
debugMsg('MyLevel='+inttostr((MyLevel)));
debugMsg('MyCurHP='+inttostr((MyCUR_HP)));
debugMsg('My=MaxHP'+inttostr((MyMAX_HP)));
debugMsg('MyCurMP='+inttostr((MyCUR_MP)));
debugMsg('MyMaxMP='+inttostr((MyMAX_MP)));
}end;
//......................................//***************************************************************{
18=StatusUpdate:d(ObjectID)d(AttribCount:Loop.01.0002)d(AttrID:Get.FSup)d(AttrValue)
}procedure StatusUpdate;
{обновление данных о себе}var
i: integer;
beginfor i:=0to ReadD(6)-1docase pck[i*8+10] of
#$01: MyLevel:=ReadD(i*8+14);
#$09: MyCur_HP:=ReadD(i*8+14);
#$0A: MyMax_HP:=ReadD(i*8+14);
#$0B: MyCur_MP:=ReadD(i*8+14);
#$0C: MyMax_MP:=ReadD(i*8+14);
#$0D: MySP:=ReadD(i*8+14);
#$0E: MyCur_Load:=ReadD(i*8+14);
#$0F: MyMax_Load:=ReadD(i*8+14);
#$21: MyCur_CP:=ReadD(i*8+14);
#$22: MyMax_CP:=ReadD(i*8+14);
end;
end;
//......................................//******************************************************************************// Парсер/Исполнитель: главный цикл обработки команд Валкера//******************************************************************************function OnExecute(Sender: TObject): integer; //CommandList: TStringListvar
s, cmd, param : string;
begin
debugMsg('Парсер команд валкера...');
ExecuteTimer.Enabled:=false; //остановим на время интерпретации и выполнения команды
result:=-1;
//проверка на наличие команд в скриптеif WalkerScript.Count=0thenbegin
result:=0;
debugMsg('В скрипте нет данных для выполнения!');
exit;
end;
try
ExecuteTimer.Interval:=DefaultExecuteDelay; //восстанавливаем скорость исполнения скрипта Валкера//на каждом шаге работаем с одной командой
s:=WalkerScript[strIndex]; //считываем команду из листа
debugMsg('Команда скрипта: '+s);
cmd:=UpperCase(RtrimEx(ExtractName(s, '('), '(')); //выцепляем команду
debugMsg('Команда: '+cmd);
param:=RtrimEx(ExtractValue(s, '('),')'); //выцепляем параметр
debugMsg('Параметр: '+param);
case cmd of'GOHOME': begin{Переход к началу скрипта после смерти}//debugMSG('переходим к началу скрипта');//strIndex:=0; //устанавливаем новую строку для исполнения//debugMSG('strIndex='+inttostr(strIndex));//RequestRestartPoint(5); //7D=RequestRestartPoint:d(PointType)end;
'JMP': begin{безусловный переход на метку}//param:=RtrimEx(ExtractName(param,')'),')');//debugMSG('результат='+param);
param:=WalkerLabel.Values[param]; //ищет по строке, номер строки (label=10)//tmp:=WalkerLabel.Names[param]; //ищет по номеру
debugMSG('переходим на строку='+param);
strIndex:=strtoint(param); //устанавливаем новую строку для исполнения
debugMSG('strIndex='+inttostr(strIndex));
end;
'CALL': begin{безусловный переход на метку}//param:=RtrimEx(ExtractName(param,')'),')');//debugMSG('результат='+param);
ReturnStack.Clear; //очищаем стек возврата из подпрограмм
ReturnStack.Add('return='+inttostr(strIndex)); //сохраним адрес возврата
ReturnStack.SaveToFile(PathWalkerScript+'return');
debugmsg('return='+inttostr(strIndex));
param:=WalkerLabel.Values[param]; //ищет по строке, номер строки (label=10)
debugMSG('переходим на строку='+param);
strIndex:=strtoint(param); //устанавливаем новую строку для исполнения
debugMSG('strIndex='+inttostr(strIndex));
end;
'RETURN': begin{безусловный переход на метку}
param:=ReturnStack.Values['return']; //ищет по строке, номер строки (label=10)
debugMSG('переходим на строку='+param);
strIndex:=strtoint(param); //устанавливаем новую строку для исполнения
debugMSG('strIndex='+inttostr(strIndex));
end;
'EXIT': begin{завершение работы скрипта}
ExecuteTimer.Enabled:=False;
debugMSG('Script stopped!');
exit;
end;
'PAUSE','DELAY': begin{изменяем паузу между командами}
ExecuteTimer.Interval:=StrToInt(AlltrimEx(param,'(',')')); //время задержкиend;
'DLGSEL': begin{находим bypass в HTML по заданной строке}
param:=GetBypass(sHtml, param);
RequestByPassToServer(param); //выбрали пункт меню
debugMSG('результат Bypass='+param);
sHTML:=''; //обнуляем после использованияend;
'NPCDLG', 'NPCSEL': begin{делаем таргет на NPC с ID в команде
NPCSEL(Marcela[ID=32173])
NPCDLG(Marcela[ID=32173])
}
param:=GetID(param);
debugMSG('результат ID='+param);
Target(GetNpcOID(strtoint(param)));
end;
'USEITEM': begin{используем предмет с ID
USEITEM(Kamael Village Teleportation Scroll[ID=12753])
}
param:=GetID(param);
debugMSG('результат ID='+param);
// ищем по id,возвращаем OID
UseItem(GetItem(strtoint(param),3,2),0);
end;
'MOVETO': begin{MOVETO(x,y,z)}
GetXYZ(param);
debugMSG('результат X='+inttostr(x1)+' Y='+inttostr(y1)+' Z='+inttostr(z1));
ExecuteTimer.Enabled:=False;
MoveTimer.Enabled:=True;
MoveBackwardToLocation(x1,y1,z1);
//runSpd:=115;//MoveTimer.Interval:=1000; //delta(myx, myy, x1, y1)*runSpd div 5000; //время задержки//debugMSG('результат T='+inttostr(ExecuteTimer.Interval)+' delta='+inttostr(delta(myx, myy, x1, y1)));//скорость = расстояние / время//время = расстояние * скорость * коэфф{
by Xkor
я в боте скорость вычисляю так:
Код:
if Boolean(Running) then speed:=runSpd
else speed:=walkSpd;
r:=speed*d*MovementSpeedMultiplier;
d - время прошедшее после с последнего обновления координат
r - сосбно смещение за это время
формула работает вроде правильно, рассинхронизации координат с сервером практически непроисходит
}end;
'ITEMCOUNT': begin//ITEMCOUNT(Название предмета[ID=#],<,1)//{//}//Подсчитывает количество указанных предметов с условиями <, >, = и выполняет скрипт в фигурных скобках.ifnot GetItemcount(param)//возвращает Истина, если проверку прошли, инача Ложьthen SkipFS; //пропускаем фигурные скобки//else inc(strIndex); //пропускаем {end;
'BUYITEM': begin{
BuyItem(Название предмета[ID=#],#)
//Покупает предмет
BuyItem(Название предмета[ID=#],#;Название предмета[ID=#],#)
//Покупает за раз более одного предмета (можно указать много предметов через точку с запятой) .
}
RequestBuyItem(param);
end;
'SELLITEM': begin{
SellItem(Название предмета[ID=#],#)
//Продаёт предмет
SellItem(Название предмета[ID=#],#;Название предмета[ID=#],#)
//Продаёт за раз более одного предмета (можно указать много предметов через точку с запятой) .
}
RequestSellItem(param);
end;
'MSG', 'SAY' : begin//MSG(Sdajom quest)
SendMessage(param); //шлем сообщение в чат
sendMSG(param); //шлем сообщение в пакетхакend;
'CHARSTATUS': begin//CharStatus([CHP|CMP|HP|MP|WEIGHT|LV|SP|RACE|STAND],[>=|>|==|!=|<|<=],число)//{//}{
CHP = Здоровье персонажа
CMP = Мана персонажа
HP = Здоровье (%)
MP = Мана (%)
WEIGHT = Загрузка (%)
LV = Уровень (вожможно)
SP = Хрен знает что такое
RACE = Это еще менее понятно
STAND = Проверка сидишь или стоишь
...,==,0) Сидишь
...,==,1) Стоишь (может быть 0 и 1 надо поменять местами
}ifnot GetCharStatus(param)//возвращает Истина, если проверку прошли, инача Ложьthen SkipFS; //пропускаем фигурные скобки//else inc(strIndex); //пропускаем {end;
//'{','}' : begin {ничего не делаем} end;'POSINRANGE': begin//PosInRange(-124658,79914,-3586,5000)//{//}
GetXYZD(param);
debugMSG('результат X='+inttostr(x1)+' Y='+inttostr(y1)+' Z='+inttostr(z1)+' D='+inttostr(delta1));
ifnot PosInRange(x1,y1,z1,delta1)//возвращает Истина, если проверку прошли, инача Ложьthen SkipFS; //пропускаем фигурные скобкиend;
'POSOUTRANGE': begin//PosOutRange(-124658,79914,-3586,5000)//{//}
GetXYZD(param);
debugMSG('результат X='+inttostr(x1)+' Y='+inttostr(y1)+' Z='+inttostr(z1)+' D='+inttostr(delta1));
ifnot PosOutRange(x1,y1,z1,delta1)//возвращает Истина, если проверку прошли, инача Ложьthen SkipFS; //пропускаем фигурные скобкиend;
'ENCHANTITEM': begin{Заточим предмет с ID
USEITEM(Enchant Scroll[ID=729];Enchant Item[ID=80])
}{возвращает результат не заточки, а того, что вещь, которую надо точить, в рюкзаке была или нет!}ifnot RequestEnchantItem(param)//возвращает Истина, если вещь для заточки нашлась, иначе если вещи не нашлось - Ложьthen SkipFS; //пропускаем фигурные скобкиend;
//************************************************************************'CRISTALLIZEITEM': begin{ разобъем предмет на кристаллы с ID
CrystallizeItem(Name1[Id=XXX],Count)
CrystallizeItem(Name1[Id=XXX],Count;Name2[Id=XXX],Count)
}
RequestCrystallizeItem(param);
end;
//************************************************************************'SET': begin{
SET (MON, ATTACK|NOATTACK|ATTACKONE, Name [ ID=n ]; Name1 [ ID=n ] |*)
Explained: The establishment attack monster or does not attack the monster
ATTACK// is the attack
NOATTACK// is does not attack
ATTACKONE// is the attack completes the order form
* - всех, кто шевелится =)
}//SET(MON,ATTACK,Gremlin[ID=20001];Goblin[ID=20003]){
Set(FightStart|FightStop)
Разрешает или запрещает Режим Боя.
Set(RangeType,DefPos,x,y,z,radii)
Выставляет режим боя по заданной центральной точке, и задает центральную точку
боя по координатам x, y и z, с радиусом указанным в radii.
Set(RangeType,StartPos,radii)
Начинает бой указывая текущую позицию центральной как если бы вы выбрали
"Combat Begin Point is Center Point" с радиусом заданным на месте <radii>.
}
SetupAlexus(param);
end;
end;
inc(strIndex);
result:=1;
ExecuteTimer.Enabled:=true; //включим интерпретацию скрипта валкераexcept
debugMsg('Error in OnExecute! Script stopped!');
end;
end;
//управление Ботом - Алексусprocedure SetupAlexus(sData: string);
//SET(MON,ATTACK|NOATTACK|ATTACKONE,Name[ID=n];Name1[ID=n];|*)//{//}//в sData = MON,ATTACK,Name[ID=n],Name1[ID=n]var
i, j, k, id, x, y, z, radius: integer;
cmd1, cmd2, cmd3, s: string;
eos : boolean;
res:variant;
BaseIdNPCs: array[1..10] ofinteger; //ID для NPC вокругfunction TestPovtor (id: integer) : integer; // функция проверяет наличие заданного моба в БДvar
i: integer;
begin
result:=0;
for i:=1to10doif BaseIdNPCs[i]=id then// Ищем нужный ID в нашей БДbegin
result:=i; // И возвращаем его индекс по БДbreak;
end;
end;
begin
debugMsg('SetupAlexus');
for i:=1to10do BaseIdNPCs[i]:=0; //забиваем нулями
s:=sData+','; //чтобы можно было взять число в конце
eos:=false;
cmd1:=RTrimEx(ExtractName(s, ','), ','); //получили строку вплодь до найденного символа
cmd1:=UpperCase(alltrim(cmd1));
sendmsg(cmd1);
case cmd1 of//RangeType,DefPos,x,y,z,radii,//Выставляет режим боя по заданной центральной точке, и задает центральную точку//боя по координатам x, y и z, с радиусом указанным в radii.//Set(RangeType,StartPos,radii)//Начинает бой указывая текущую позицию центральной как если бы вы выбрали//"Combat Begin Point is Center Point" с радиусом заданным на месте <radii>.'RANGETYPE': begin
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
cmd2:=RTrimEx(ExtractName(s, ','), ','); //получили строку вплодь до найденного символа
cmd2:=UpperCase(alltrim(cmd2));
sendmsg(cmd2);
case cmd2 of'STARTPOS': begin
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
radius:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили строку вплодь до найденного символа
sendmsg(INTTOSTR(RADIUS));
res:=CallSF('Locomotiv-05_wsr', 'setStartPos', ['',radius]);
sendmsg(res);
end;
'DEFPOS': begin
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
x:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили строку вплодь до найденного символа
sendmsg(INTTOSTR(X));
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
y:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили строку вплодь до найденного символа
sendmsg(INTTOSTR(Y));
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
z:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили строку вплодь до найденного символа
sendmsg(INTTOSTR(Z));
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
radius:=strtoint(RTrimEx(ExtractName(s, ','), ',')); //получили строку вплодь до найденного символа
sendmsg(INTTOSTR(RADIUS));
res:=CallSF('Locomotiv-05_wsr', 'setDefPos', ['',x,y,z,radius]);
sendmsg(res);
end;
end;
end;
'FIGHTSTART': begin
res:=CallSF('Locomotiv-05_wsr', 'setPauseGame', ['']);
sendmsg(res);
end;
'FIGHTSTOP': begin
res:=CallSF('Locomotiv-05_wsr', 'setResumeGame', ['']);
sendmsg(res);
end;
'MON': begin
s:=ExtractValue(s, ','); //получили остаток строки начиная с искомого символа
cmd2:=RTrimEx(ExtractName(s, ','), ','); //получили строку вплодь до найденного символа
cmd2:=UpperCase(alltrim(cmd2));
sendmsg(cmd2);
case cmd2 of//MON,ATTACK,Name[ID=n];Name1[ID=n],'ATTACK': begin
j:=1; //кол-во монстров
cmd3:=RTrimEx(ExtractValue(s, ','), ','); //получили остаток строки начиная с искомого символа
cmd3:=UpperCase(alltrim(cmd3));
if cmd3<>'*'thenbegin
sendmsg(cmd3);
whilenot eos dobeginif s<>''thenbegin
s:=ExtractValue(s,'='); //получили остаток строки начиная с искомого символа
id:=strtoint(RTrimEx(ExtractName(s, ']'), ']')); //получили строку вплодь до найденного символа//командуем скрипту Locomotiv-05_wsr установить переменную NpcTypeIDxx в величину id
res:=CallSF('Locomotiv-05_wsr', 'setVar', ['NpcTypeID'+inttostr(j), id]);
sendMSG(res);
inc(j);
s:=ExtractValue(s,';'); //получили остаток строки начиная с искомого символаif i>10then eos:=true; //не более 10 мобовendelse eos:=true; //сигналим, что закончили поискend;
endelsebegin//иначе, обработка * - звездочки, отлавливая пакеты NpcInfo в количестве не более 10
sendmsg(cmd3);
j:=1;
for i:=1to maxnpc do//maxnpc dobegin
sendmsg('i='+inttostr(i)+' id='+inttostr(BaseNPCs[i,2])+' attcbl='+inttostr(BaseNPCs[i,3]));
if(BaseNPCs[i,1]=0)thenbreak; //проверяем, чтоб OID мобов не равнялось 0 //sendmsg('i='+inttostr(i)+' j='+inttostr(j)); //+'BaseNPCs[i,3]='+inttostr(BaseNPCs[i,3]));if(BaseNPCs[i,3]=1)and(j<=10)then//проверяем, чтоб мобов было не более 10 и были атакуемыbeginif testpovtor(BaseNPCs[i,2])=0thenbegin
BaseIdNPCs[j]:=BaseNPCs[i,2];
id:=BaseNPCs[i,2];
//командуем скрипту Locomotiv-05_wsr установить переменную NpcTypeIDxx в величину id
sendmsg('NpcTypeID'+inttostr(j)+' id='+inttostr(id));
res:=CallSF('Locomotiv-05_wsr', 'setVar', ['NpcTypeID'+inttostr(j), id]);
sendMSG(res);
//povtor:=true;inc(j);
end;
end;
end;
end;
end;
//MON,NOATTACK,*,'NOATTACK': begin
s:=RTrimEx(ExtractName(s, ','), ','); //получили строку вплодь до найденного символаif s='*'thenbeginfor i:=1to10dobegin//командуем скрипту Locomotiv-05_wsr установить переменную NpcTypeIDxx в величину id
res:=CallSF('Locomotiv-05_wsr', 'setVar', ['NpcTypeID'+inttostr(i), 0]);
sendMSG(res);
end;
endelse sendMSG('команда не поддерживается!');
end;
end;
end;
end;
end;
function GetMobID(s : string): string;
var res: variant;
begin
res:=CallSF ('Locomotiv-05_wsr', 'getVar', [s]);
result:=res;
sendMSG('получили MobID='+res);
end;
procedure SetMobID(s: string; i: integer);
var res: variant;
begin
res:=CallSF ('Locomotiv-05_wsr', 'setVar', [s, i]);
sendMSG(res);
end;
procedure UserCommands; //комманды пользователяvar
i: integer;
s, cmd1: string;
begin//если комманда обработана удачно, то в чат сообщение не попадет, а будет выдано системное сообщение прямо в клиент// if InitMode then
s:=ReadS(2);
s:=s+'='; //чтобы можно было взять число в конце
cmd1:=RTrimEx(ExtractName(s, '='), '='); //получили строку вплодь до найденного символа
cmd1:=UpperCase(alltrim(cmd1));
sendmsg(cmd1);
sendmessage(cmd1);
case cmd1 of//команда загрузки скрипта script|load=walkerscript'SCRIPT','LOAD': begin
s:=ExtractValue(s, '='); //получили остаток строки начиная с искомого символа
s:=RTrimEx(ExtractName(s, '='), '='); //получили строку вплодь до найденного символа
sendmsg(s);
sendmessage(s);
WalkerScript.clear;
WalkerScript.LoadFromFile(PathWalkerScript+s+'.sec'); //загружаем скрипт
MakeLabel; //готовим адреса меток из скрипта Валкера
pck:='';
end;
'START','RUN': begin
strIndex:=0; //начинаем с первой строки
ExecuteDelay:=DefaultExecuteDelay; //задержка между командами валкера
ExecuteTimer.Enabled:=true; //включим интерпретацию скрипта валкера
pck:='';
end;
'STOP': begin
CallSF('Locomotiv-05_wsr', 'setStopGame', ['']); //остановим бота
ExecuteTimer.Enabled:=false; //выключим интерпретацию скрипта валкера
pck:='';
end;
'PAUSE': begin
CallSF('Locomotiv-05_wsr', 'setPauseGame', ['']); //остановим бота
ExecuteTimer.Enabled:=false; //выключим интерпретацию скрипта валкера
pck:='';
end;
'RESUME': begin
CallSF('Locomotiv-05_wsr', 'setResumeGame', ['']); //остановим бота
ExecuteTimer.Enabled:=false; //выключим интерпретацию скрипта валкера
pck:='';
end;
end;
end;
//******************************************************************************{
основная часть скрипта, вызывается при приходе каждого пакета, если скрипт включен
}//******************************************************************************begin//****************************************************************************//не обрабатываем пустые пакетыif pck=''thenexit;
//****************************************************************************if(ConnectName=Name)and FromServer thenbegincase pck[1] of//************************************************************************//07=BuyList
#$11: begin//abyss//#$07: begin//debugmsg('S>C $07 BuyList');
BuyList;
end;
//************************************************************************//08=DeleteObject:d(ObjectID)d(0)
#$12: begin//abyss//#$08: begin//debugmsg('S>C $08 DeleteObject '+inttostr(ReadD(2)));
DeleteNpc(ReadD(2));
end;
//************************************************************************//0C=NpcInfo:d(ObjectID)d(NpcTypeId:Get.NpcId)d(IsAttackable)d(X)d(Y)d(Z)
#$16: begin//abyss//#$0C: begin//debugmsg('S>C Пакет NpcInfo #$0C'+inttostr(ReadD(2)));// OID ID
AppendNpc(ReadD(2), ReadD(6)-kID, ReadD(10)); //добавляем в базу данныхend;
//************************************************************************
#$1B: begin//abyss//#$11: begin//debugmsg('S>C Пакет ItemListPacket #$11');
ItemList;
end;
//************************************************************************//18=StatusUpdate//#$18: begin
#$0E: begin//abyss//debugmsg('S>C Пакет StatusUpdate #$18');
StatusUpdate;
end;
//************************************************************************//19 (NpcHtmlMessage)
#$0F: begin//abyss//#$19: begin//debugmsg('S>C Пакет NpcHtmlMessage #$19');
sHTML:=ReadS(6);
//sendmsg(sHTML);end;
//************************************************************************
#$27: begin//abyss//#$21: begin//debugmsg('S>C Пакет InventoryUpdate #$21');
InventoryUpdate;
end;
//************************************************************************//32=UserInfo//#$32: begin
#$04: begin//abyss//debugmsg('S>C Пакет UserInfo #$33');
UserInfo;
end;
end; //caseend;
//****************************************************************************if(ConnectName=Name)and FromClient thenbegincase pck[1] of//************************************************************************
#$49: UserCommands; //Say2:s(Text)d(Type)s(Target)//ValidatePosition пакет от клиента с моими кординатами//59=ValidatePosition:d(X)d(Y)d(Z)d(Heading)d(Data)
#$59: begin//debugmsg('C>S Пакет ValidatePosition #$59');
MyX:=ReadD(2); //получаю координату х моего чара
MyY:=ReadD(6); //получаю координату у моего чара
MyZ:=ReadD(10); //получаю координату z моего чараend;
end; //caseend;
end.
//******************************************************************************{ Changelog.txt }{
version 0.10 от 30.04.2009г.
[+] Команды управления из чата START|RUN, STOP, PAUSE, RESUME, загрузка скрипта SCRIPT|LOAD=TEST
[+] Добавил управление ботом - Алексус, который пришлось модифицировать для поодержки данного скрипта
[+] Добавил POSOUTRANGE, POSINRANGE
[+] CharStatus([CHP|CMP|HP|MP|WEIGHT|LV|SP|RACE|STAND],[>=|>|==|!=|<|<=],число)
[+] Добавил UserInfo, StatusUpdate
[+] Добавил SET(MON,ATTACK|NOATTACK|ATTACKONE,Name[ID=n];Name1[ID=n];|*), SET(MON,NOATTACK,*)
[+] Добавил SET(RangeType,DefPos,x,y,z,radii), SET(RangeType,StartPos,radii)[*] мелкие правки кода[*] вложенные условия[*] поменял логику применения RequestEnchantItem
version 0.9 от 02.02.2009г.[*] Поправил ITEMCOUNT
version 0.8 от 02.02.2009г.
[+] Понимает команды:
CrystallizeItem(Name1[Id=XXXX],Count;Name2[Id=XXXX],Count)
version 0.7 от 20.01.2009г.
[+] Понимает команды:
MSG(сообщение),
SELLITEM(Name1[Id=XXXX],Count) или SELLITEM(Name1[Id=XXXX],Count;Name2[Id=XXXX],Count),
DLGSEL(теперь и кнопки жмёт)
version 0.6 от 19.01.2009г.
[+] Понимает команды:
BUYITEM(Name1[Id=XXXX],Count) или BUYITEM(Name1[Id=XXXX],Count;Name2[Id=XXXX],Count)
version 0.5 от 18.01.2009г.
[+] Понимает команды:
PAUSE(время в мс),
ITEMCOUNT(Name[ID=XXXX],==|=>|<=|!=|<>,Count)
version 0.4 от 18.01.2009г.
[+] Понимает команды:
MOVETO(x,y,z),
USEITEM(Name[Id=XXX]),
CALL(метка),
RETURN
version 0.3 от 17.01.2009г.
[+] Понимает команды:
NPCDLG(Name[Id=XXX]),
NPCSEL(Name[Id=XXX])
version 0.2 от 16.01.2009г.
[+] Понимает команды:
DELAY(время в мс),
JMP(метка),
LABEL(имя метки),
EXIT,
DLGSEL(только текст, кнопки не жмёт)
[+] Ведём базу инвентаря
[+] Ведём базу NPCs
version 0.1 от 14.01.2009г.
[!] пока только заготовки:
NPCDLG(Name[Id=XXX]),
NPCSEL(Name[Id=XXX]),
USEITEM(Name[Id=XXX]),
CALL(метка)
}
Const ProgramName =
'Бот - локомотив от Alexus '+
'версия : 0.5.2 (WSR) '+
'дата: 22.01.09';
//by NLObP//Исправлено для работы совместно со скриптом WalkerScriptRunner//30.04.2009{#############################################################################
Описание:
Бот для соло-кача в принципе любого война. По функциональности (в режиме кача) это еще не Волкер, но скрипт уже довольно умный :)
Охотиться только на заданных мобов.
Возможности:
- Управляется из скрипта WalkerScriptRunner
- запоминает центр и радиус кача
- запоминает список мобов на которых надо охотиться!
- запоминает и затем подбирает дроп, упавший с убитых им мобов
- умеет лечиться банками (тип банки легко настраивается), ведется учет количества оставшихся банок
- управляется из окна чата в самом клиенте
- имеет окно для вывода всех параметров и статистики
- если моб по какой-то причине стал недосягаемым (стоит за деревом), то через 1 мин БОТ переключится на другого моба.
- если бот умирает, то не палимся и прекращаем рыпаться...(скоро появиться возможность логаута)
- пьет баф-банки
- работает под РУ-ОФФ Gracia, но для этого нужно обойти щит-консоль
- умеет спойлить (функция новая, нужно тестить)
Недостатки: (со временем будет исправлено)
- бот не умеет обходить препядствия
История версий:
0.1 - самое начало :)
0.2 - куча глюков, первая рабочая версия!
0.3 - первая стабильная безглючная версия, с минимальной функциональностью (самая стабильная, но очень простая)
0.4 - изменения:
- радиус кача можно задать в секции настройки
- бот теперь сразу видит всю локацию с самого запуска скрипта
- автоопределение размера инвентаря
- ID мобов можно теперь задавать в секции настройки
- ускорен процесс добавление мобов через клиент (теперь их не надо гасить)
- отключена ф-ция автоопределения своего ID и инвентаря, по этому запускать теперь бот надо до запуска клиента
- обновлено окно вывода информации (теперь его нельзя случайно закрыть!)
- на форму добавлены кнопки управления
- добавлена кнопка "Init" в окно управления, теперь бота можно запускать в любом месте, затем нажать эту кнопку
- добавлен механизм глотания баф-банок
- оптимизирован алгоритм выбора ближайшего моба
- скрипт доработан для новых версий пакетхака (данная версия разработана под пакетхак версии 3.4.1.69)
- в режиме кача, при отсутствии мобов в радиусе, бот возвращается в центр кача
- устранена ошибка, связянная с неправильным распознованием пакета 01 - MoveToLocation
0.5 - модификация под РУ-ОФФ Gracia
- добавлен споил
- убрана ошибка, вылезающая после нажатия кнопки Init
- немного подправлен алгоритм работы с инвентарем
Инструкция:
1. Внимательно НАСТРАИВАЕМ параметры в секции настройки
(90% ошибок и последующих глюков в скрипте вылезает именно отсюда!!!), жмем кнопку "Сохранить"
2. Запускаем ПакетХак, запускаем скрипт, запускаем игрового клиента.
либо запускаем скрипт после захода в игру, и жмем кнопку на форме "Init"
3. Добираемся до места кача
4. Выбираем в таргет моба на которого хотим охотится и отправляем в общий чат-> 1
5. Выбираем следующего моба отправляем в общий чат цифру 2, и т.д. можно выбрать до 10 разных тварей.
6. Либо просто в секции настройки задаем ID мобов
7. Становимся в центр кача и отправляем в общий чат-> pos , если все правильно то в чате будет выдано сообщение, что координаты заданы.
8. Бежим к краю радиуса кача и отправляем в общий чат-> dist, в чате вылезет сообщение от системы, что радиус задан.
9. Если радиус кача задан в секции настройки, то п.7 и 8 можно пропустить!!! становимся примерно в центр кача и отправляем в общий чат-> start ,
если все сделано верно, то будет выдано соответствующее сообщение.
10.Если бот поймал моба в таргет и побежал его атаковать, то можно свернуть окно игры
и смотреть на информационное окно скрипта.
В процессе кача, в общий чат можно посылать следующие управляющие команды:
pause - пауза, можно приостановить бота, повторная комманда pause запустит бота
stop - срочная остановка бота, и сброс всех настроек, повторный запуск возможен командой start
Также можно управлять ботом одноименными кнопками с формы.
#############################################################################}//################### Сеция настройки бота ############################################
NickName='RemoteAccess'; //имя персонажа в игре//NickName='NLObP'; //имя персонажа в игре
CharNumber = 0; // если больше одного перса на аккаунте, то задаем его порядковый номер, нумерация начинается с нуля.
Radius_Kacha = 0; // Радиус кача, если 0 - то задавать вручную через клиента (изменять от 100 до 9999). Проверил на практике, что максимум не более 2000.
Spoil = false; // Споил false - откл, true - вкл. (только для гномов-спойлеров)//-------------------------------------------------------------------------------------
HPLevel = 70; // Уровень жизни в %, при котором начинаем глотать банки
HPBottleID = 0; // ItemID 1060=Lesser Healing Potion; 1061=Healing Potion; 1539=Greater Healing Potion;
DrinkDelay = 14; // минимальное время задержки повторного питься HP-бутылки (10 - 20 сек).//---------------------------------------------------------------------------------------
Bottle_1_ID = 0; // ID номера баф-банок, 6036=Greater Magic Haste Potion, 6035=Magic Haste Potion, 1374=Greater Haste Potion, 1062=Haste Potion, 734=Haste Potion
Bottle_2_ID = 0; // если 0 - то не использовать. 1375=Greater Swift Attack Potion, 735=Potion of Alacrity
Bottle_Interval = 19; // Время задержки повторного использования бутылей в минутах (изменять от 1 до 100)//----------------------------------------------------------------------------------------//############# Секция расширенной настройки для опытных ботоводеров :) ###################################
TimerCombat_Interval = 500; // частота срабатывания таймера атаки в милисекундах, чем меньше это число, тем быстрее бот будет думать.
TimerPickUp_Interval = 500; // частота срабатывания таймера сбора дропа, чем меньше это число, тем быстрее бот будет думать.
TimerForm_Interval = 2500; // частота срабатывания таймера обновления формы, чем меньше это число, тем быстрее будут обновляться данные в форме.
Attack_Time = 45; // Время на убивание моба в секундах.
PickUp_Time = 15; // Врямя на поднятие дропа в секундах.
Vertical = 1000; // Вертикальный радиус кача//---------------------------------------------------------------------------------------//########################################################################################
NpcTypeID_List_Razmer = 10; // Размер списка мобов (не трогать!)
OX = 1; OY = 2; OZ= 3; // Служебные константы
maximumItems = 500; // Размер базы мобов и дропа.
OblastVidimosti = 500000; // (не трогать!!!) Область видимости объектов (изменять от 1000 до 500000).
InvRazmer = 249; // Размер инвентаря (не трогать)
kID=1000000;
var
NpcTypeID1, // Если знаем, то задаем Type ID мобов, на которых будем охотиться
NpcTypeID2, // Если здесь одни нули, то задавать мобов будем через клиента
NpcTypeID3, // Для интерлюда задаем в формате 102xxxx , где xxxx - ID моба
NpcTypeID4, // Для С4 в формате 100хххх , где xxxx - ID моба
NpcTypeID5, // Для Gracia формат пока не смотрел...
NpcTypeID6,
NpcTypeID7,
NpcTypeID8,
NpcTypeID9,
NpcTypeID10:integer;
InitMode, RestartMode, PickUpMode : boolean;
//-------------------------------------- БД --------------------------------------------------
MobsObjID : array [1..maximumItems] ofinteger; // ID моба
MobsNpcTypeID : array [1..maximumItems] ofinteger; // Тип моба
MobsDist : array [1..maximumItems] ofinteger; // Расстояние от центра кача до моба
MobsXYZ : array [1..maximumItems, 1..3] ofinteger; // Координаты
MobsIsAttackable : array [1..maximumItems] ofboolean; // Можно ли моба атаковать
MobsAgression : array [1..maximumItems] ofboolean; // Моб атакует меня или стоит в сторонке...
MobsUpdated : array [1..maximumItems] ofboolean; // Метка об обновлении моба
MobsLastIndex: integer; // индекс последнего элемента базы
NpcTypeID_List : array [1..NpcTypeID_List_Razmer] ofinteger; // Список мобов (коды мобов)
NpcTypeID_List_Count : integer; // Текущий размер списка
NpcTypeID_CurrentMob : integer;
Items_ObjectID : array [1..maximumItems] ofinteger; // БД дропа с мобов
Items_ItemID : array [1..maximumItems] ofinteger;
Items_XYZ : array [1..maximumItems, 1..3] ofinteger;
ItemsLastIndex: integer;
Inventory: array[0..InvRazmer, 0..9] ofinteger; // инвентарь (itemType1, ObjectID, ItemID, count, itemType2, CustType1, isEquipped, BodyPart, EnchantLevel, CustType2)
CurrentInvRazmer : integer;
HPBottleObjID, HPBottleCount : integer; // ObjID и количество HP-бутылей
Bottle_1_ObjID, Bottle_1_Count : integer;
Bottle_2_ObjID, Bottle_2_Count : integer;
TargetID, LastKilledMobObjID : integer; // Текущая цель, последний убитый моб
MobsKilled : integer; // счетчик убитых мобов
CenterX, CenterY, CenterZ : integer; // Центр кача
CenterFixed : boolean;
Radius: integer; // Радиус кача//--------------------------------------------------------
MyX, MyY, MyZ : integer; // Мои статы
MyID, MyHP, MyMaxHP: integer;
MyMP, MyMaxMP, MyCP, MyMaxCP: integer;
HPlevelProcent : integer; // уровень жизни в прочентах//--------------------------------------------------------
frm: TForm; // переменные описания формы
log, MobsDBscreen, ItemsDBScreen: TMemo;
panel: TPanel;
btnInit, btnStart, btnStop, btnPause: TButton;
frmParamIndex: byte;
textX, textY, textZ, textMyID, textMyHP, textMyMaxHP: TEdit;
textMyMP, textMyMaxMP, textMyCP, textMyMaxCP: TEdit;
textCenterX, textCenterY, textCenterZ, textRadius : TEdit;
textTargetID, textMobX, textMobY, textMobZ : TEdit;
textAttackCycle, textMobsLastIndex, textMobsKilled: TEdit;
textHPBottleCount, textBottle_1_Count, textBottle_2_Count: TEdit;
cbFilterRadius : TCheckBox; l1: Tlabel;
//----------------------------------------------------------
TimerForm, TimerCombat, TimerPickUp, TimerBafBanka, TimerCheckDB : TTimer; // таймеры
time1: integer;
Calculated_AttackTime, Calculated_PickUpTime : integer;
AttackCycle: integer; // Цикл атаки
Spoiled: boolean;
//##############################################################################################//поддержка Walkerscriptrunnerfunction setStartGame(s:string) : variant;
begin
StartGame;
result:='StartGame';
end;
function setStopGame(s:string) : variant;
begin
StopGame;
result:='StopGame';
end;
function setPauseGame(s:string) : variant;
begin
PauseGame;
result:='PauseGame';
end;
function setResumeGame(s:string) : variant;
begin
ResumeGame;
result:='ResumeGame';
end;
function setCenter(x,y,z : integer) : variant;
beginif(not CenterFixed)and InitMode thenbegin
CenterX:= X;
CenterY:= Y;
CenterZ:= Z;
CenterFixed:=true;
SendMsg('Центр кача задан успешно! x='+inttostr(x)+' y='+inttostr(y)+' z='+inttostr(z));
result:='SetCenter';
end;
end;
function setRadius(radius : integer) : variant;
beginif InitMode thenbegin
Radius:= radius;
SendMsg('Радиус кача задан успешно, R = '+ inttostr(Radius));
result:='SetRadius';
end;
end;
function setDefPos(s : string; x,y,z,r : integer) : variant;
beginif(not CenterFixed)and InitMode thenbegin
CenterX:= X;
CenterY:= Y;
CenterZ:= Z;
CenterFixed:=true;
SendMsg('Центр кача задан успешно! x='+inttostr(x)+' y='+inttostr(y)+' z='+inttostr(z));
Radius:= r;
SendMsg('Радиус кача задан успешно, R = '+ inttostr(Radius));
result:='SetDefPos';
endelse result:='SetDefPos error';
end;
function setStartPos(s : string; r : integer) : variant;
beginif(not CenterFixed)and InitMode thenbegin
CenterX:= myX;
CenterY:= myY;
CenterZ:= myZ;
CenterFixed:=true;
SendMsg('Центр кача задан успешно! x='+inttostr(myx)+' y='+inttostr(myy)+' z='+inttostr(myz));
Radius:= r;
SendMsg('Радиус кача задан успешно, R = '+ inttostr(Radius));
result:='SetStartPos';
endelse result:='SetStartPos error';
end;
//считываем/записываем ID мобовfunction getVar (s: string;): variant;
begincase s of'NpcTypeID1': Result:=NpcTypeID1; //Записываем ID МОБ'ов'NpcTypeID2': Result:=NpcTypeID2; //Записываем ID МОБ'ов'NpcTypeID3': Result:=NpcTypeID3; //Записываем ID МОБ'ов'NpcTypeID4': Result:=NpcTypeID4; //Записываем ID МОБ'ов'NpcTypeID5': Result:=NpcTypeID5; //Записываем ID МОБ'ов'NpcTypeID6': Result:=NpcTypeID6; //Записываем ID МОБ'ов'NpcTypeID7': Result:=NpcTypeID7; //Записываем ID МОБ'ов'NpcTypeID8': Result:=NpcTypeID8; //Записываем ID МОБ'ов'NpcTypeID9': Result:=NpcTypeID9; //Записываем ID МОБ'ов'NpcTypeID10': Result:=NpcTypeID10; //Записываем ID МОБ'овend;
end;
function setVar (s: string; i: integer): variant;
begincase s of'NpcTypeID1': NpcTypeID1:=i; //Записываем ID МОБ'ов'NpcTypeID2': NpcTypeID2:=i; //Записываем ID МОБ'ов'NpcTypeID3': NpcTypeID3:=i; //Записываем ID МОБ'ов'NpcTypeID4': NpcTypeID4:=i; //Записываем ID МОБ'ов'NpcTypeID5': NpcTypeID5:=i; //Записываем ID МОБ'ов'NpcTypeID6': NpcTypeID6:=i; //Записываем ID МОБ'ов'NpcTypeID7': NpcTypeID7:=i; //Записываем ID МОБ'ов'NpcTypeID8': NpcTypeID8:=i; //Записываем ID МОБ'ов'NpcTypeID9': NpcTypeID9:=i; //Записываем ID МОБ'ов'NpcTypeID10': NpcTypeID10:=i; //Записываем ID МОБ'овend;
ClearDB;
end;
//##############################################################################################procedure Init; //Вызывается при включении скриптаvar
i,n : integer;
begin
MyID:= 0; // обнуляем ВСЕ данные
MyX:= 0;
MyY:= 0;
MyZ:= 0;
MyID:= 0;
MyHP:= 0;
MyMaxHP:= 0;
MyMP:= 0;
MyMaxMP:= 0;
MyCP:= 0;
MyMaxCP:= 0;
time1:=1;
Calculated_AttackTime:= round(1000 / TimerCombat_Interval * Attack_Time);
Calculated_PickUpTime:= round(1000 / TimerPickUp_Interval * PickUp_Time);
HPlevelProcent:= 0;
HPBottleObjID:= 0;
HPBottleCount:= 0;
Bottle_1_ObjID:= 0;
Bottle_1_Count:= 0;
Bottle_2_ObjID:= 0;
Bottle_2_Count:= 0;
InitMode:= true;
RestartMode:= false;
MobsLastIndex:= 0;
for i:=1to maximumItems do// Очищаем базуbegin
MobsObjID[i]:= 0;
MobsNpcTypeID[i]:=0;
MobsDist[i]:= 0;
MobsXYZ[i, OX]:= 0;
MobsXYZ[i, OY]:= 0;
MobsXYZ[i, OZ]:= 0;
MobsIsAttackable[i]:= false;
MobsAgression[i]:= false;
MobsUpdated[i]:=false;
end;
for i:=0to InvRazmer dofor n:=0to9do Inventory[i, n]:= 0;
CurrentInvRazmer:=0;
TimerCombat:=TTimer.Create(nil); // создаем таймеры
TimerCombat.OnTimer:=@OnTimerCombat;
TimerCombat.enabled:=false;
TimerCombat.interval:= TimerCombat_Interval;
TimerForm:=TTimer.Create(nil);
TimerForm.OnTimer:=@OnTimerForm;
TimerForm.enabled:=true;
TimerForm.interval:= TimerForm_Interval;
TimerPickUp:=TTimer.Create(nil);
TimerPickUp.OnTimer:=@OnTimerPickUp;
TimerPickUp.enabled:=false;
TimerPickUp.interval:= TimerPickUp_Interval;
TimerBafBanka:=TTimer.Create(nil);
TimerBafBanka.OnTimer:=@OnTimerBafBanka;
TimerBafBanka.enabled:=false;
TimerBafBanka.interval:= 15000;
TimerCheckDB:=TTimer.Create(nil);
TimerCheckDB.OnTimer:=@OnTimerCheckDB;
TimerCheckDB.enabled:=true;
TimerCheckDB.interval:= 20*60*1000; // раз в 20 минут
frmParamIndex:=0; // создаем контролы в форме
frm:= TForm.Create(nil);
frm.Caption:= ProgramName;
frm.BorderStyle := bsDialog;
frm.Position := poScreenCenter;
frm.Width:=650;
frm.Height:=700;
frm.OnClose := @FormClose;
MobsDBscreen:=TMemo.Create(frm);
MobsDBscreen.parent:=frm;
MobsDBscreen.ReadOnly:=true;
MobsDBscreen.ScrollBars:=0;
MobsDBscreen.Top:=157;
MobsDBscreen.Width:=457;
MobsDBscreen.Height:=385;
ItemsDBscreen:=TMemo.Create(frm);
ItemsDBscreen.parent:=frm;
ItemsDBscreen.ReadOnly:=true;
ItemsDBscreen.ScrollBars:=0;
ItemsDBscreen.Top:=1;
ItemsDBscreen.Width:=300;
ItemsDBscreen.Height:=155;
panel:=TPanel.Create(frm);
panel.parent:=frm;
panel.align:=alRight;
log:=TMemo.Create(panel);
log.parent:=frm;
log.align:=alBottom;
log.ReadOnly:=true;
log.ScrollBars:=2;
log.Width:=570;
log.Height:=100;
log.Lines.Add('Лог...');
btnInit:= TButton.Create(frm);
btnInit.Name := 'btnInit';
btnInit.Parent := frm;
btnInit.SetBounds(370, 10, 75, 25);
btnInit.Caption := 'Init';
btnInit.OnClick := @BtnInit_Click;
btnStart:= TButton.Create(frm);
btnStart.Name := 'btnStart';
btnStart.Parent := frm;
btnStart.SetBounds(370, 40, 75, 25);
btnStart.Caption := 'Start';
btnStart.OnClick := @btnStart_Click;
btnStop:= TButton.Create(frm);
btnStop.Name := 'btnStop';
btnStop.Parent := frm;
btnStop.SetBounds(370, 70, 75, 25);
btnStop.Caption := 'Stop';
btnStop.OnClick := @btnStop_Click;
btnPause:= TButton.Create(frm);
btnPause.Name := 'btnPause';
btnPause.Parent := frm;
btnPause.SetBounds(370, 100, 75, 25);
btnPause.Caption := 'Pause';
btnPause.OnClick := @btnPause_Click;
l1:= TLabel.Create(frm);
l1.caption:='Фильтры:';
l1.parent:=frm;
l1.SetBounds(5, 547, 97, 17);
cbFilterRadius := TCheckBox.Create(frm);
cbFilterRadius.Name := 'cbFilterRadius';
cbFilterRadius.Parent := frm;
cbFilterRadius.SetBounds(60, 545, 97, 17);
cbFilterRadius.Caption := 'Радиус';
// cbFilterRadius.OnClick := @CheckBoxFilterClick;
textMyID:= CreateTextBox('textMyID');
CreateLabel('Мой ID :');
textX:= CreateTextBox('textX');
CreateLabel('Мой X :');
textY:= CreateTextBox('textY');
CreateLabel('Мой Y :');
textZ:= CreateTextBox('textZ');
CreateLabel('Мой Z :');
textMyHP:= CreateTextBox('textMyHP');
CreateLabel('Мой HP :');
textMyMaxHP:= CreateTextBox('textMyMaxHP');
CreateLabel('Мой MaxHP :');
textMyMP:= CreateTextBox('textMyMP');
CreateLabel('Мой MP :');
textMyMaxMP:= CreateTextBox('textMyMaxMP');
CreateLabel('Мой MaxMP :');
// textMyCP:= CreateTextBox('textMyCP');// CreateLabel('Мой CP :');// textMyMaxCP:= CreateTextBox('textMyMaxCP');// CreateLabel('Мой MaxCP :');inc(frmParamIndex);
textCenterX:= CreateTextBox('textCenterX');
CreateLabel('Ц. кач Х:');
textCenterY:= CreateTextBox('textCenterY');
CreateLabel('Ц. кач Y:');
textCenterZ:= CreateTextBox('textCenterZ');
CreateLabel('Ц. кач Z:');
textRadius:= CreateTextBox('Radius');
CreateLabel('Radius :');
inc(frmParamIndex);
textTargetID:= CreateTextBox('TargetID');
CreateLabel('Цель ID :');
textAttackCycle:= CreateTextBox('AttackCycle');
CreateLabel('Цикл атаки:');
textMobsLastIndex:= CreateTextBox('MobsLastIndex');
CreateLabel('Мобов в БД:');
textMobsKilled:= CreateTextBox('MobsKilled');
CreateLabel('Убито моб:');
inc(frmParamIndex);
textHPBottleCount:= CreateTextBox('HPBottleCount');
CreateLabel('HP бутылей:');
textBottle_1_Count:= CreateTextBox('Bottle_1_Count');
CreateLabel('Баф-Банки 1:');
textBottle_2_Count:= CreateTextBox('Bottle_2_Count');
CreateLabel('Баф-Банки 2:');
ClearDB;
frm.Show; // выводим форму на экран
buf:= #$14; // принудительно вызываем пакеты инвентаря и userinfo
SendToServerEx(NickName);
//RequestRecordInfo - прийдёт и юзеринфо, и инфа о всех нпц и игроках,
buf:=#$6E;
SendToServerEx(NickName);
end;
procedure btnPause_Click(Sender: TButton);
begin
PauseGame;
end;
procedure btnStop_Click(Sender: TButton);
begin
StopGame;
end;
procedure btnStart_Click(Sender: TButton);
begin
StartGame;
end;
procedure BtnInit_Click(Sender: TButton);
begin
RestartMode:= true;
//buf:= #$57; // авто релогин)//buf:= #$0F; // принудительно вызываем пакеты инвентаря и userinfo//RequestRecordInfo - прийдёт и юзеринфо, и инфа о всех нпц и игроках,
buf:=#$6E;
SendToServerEx(NickName);
end;
procedure AddtoNpcTypeID_List (NpcTypeID: integer);
begininc(NpcTypeID_List_Count);
NpcTypeID_List[NpcTypeID_List_Count]:= NpcTypeID;
end;
procedure ClearDB; // Очистка БДvar// Надо все занулять, иначе там хрень всякая вылезает или старые данные
i : integer;
begin
ItemsLastIndex:= 0; // Очищаем переменные
TargetID:= 0;
LastKilledMobObjID:= 0;
MobsKilled:= 0;
CenterX:= 0;
CenterY:= 0;
CenterZ:= 0;
CenterFixed:= false;
Radius:= Radius_Kacha;
TimerBafBanka.interval:= 15000;
AttackCycle:= 0;
Spoiled:= false;
NpcTypeID_List_Count:= 0;
if NpcTypeID1 <> 0then AddtoNpcTypeID_List (NpcTypeID1);
if NpcTypeID2 <> 0then AddtoNpcTypeID_List (NpcTypeID2);
if NpcTypeID3 <> 0then AddtoNpcTypeID_List (NpcTypeID3);
if NpcTypeID4 <> 0then AddtoNpcTypeID_List (NpcTypeID4);
if NpcTypeID5 <> 0then AddtoNpcTypeID_List (NpcTypeID5);
if NpcTypeID6 <> 0then AddtoNpcTypeID_List (NpcTypeID6);
if NpcTypeID7 <> 0then AddtoNpcTypeID_List (NpcTypeID7);
if NpcTypeID8 <> 0then AddtoNpcTypeID_List (NpcTypeID8);
if NpcTypeID9 <> 0then AddtoNpcTypeID_List (NpcTypeID9);
if NpcTypeID10<> 0then AddtoNpcTypeID_List (NpcTypeID10);
if NpcTypeID_List_Count = 0thenfor i:=1to NpcTypeID_List_Razmer do NpcTypeID_List[i]:= 0;
NpcTypeID_CurrentMob:= 0;
for i:=1to maximumItems do// Очищаем базуbegin
Items_ObjectID[i]:= 0;
Items_ItemID[i]:= 0;
Items_XYZ[i, OX]:= 0;
Items_XYZ[i, OY]:= 0;
Items_XYZ[i, OZ]:= 0;
end;
btnStop.enabled:=false;
btnPause.enabled:= false;
end;
function Wait(var tick: integer; Timewait: Integer): Boolean; // сквозная проверка без остановки скрипта (c)dmitry501, modifed by Sh00rGovar
t: integer;
begin
result:=false;
t:=Round(Time*86400);
if t>(tick+Timewait)thenbeginif tick>0then result:=true;
tick:=t;
end;
end;
//############################## Модуль работы с Инвентарем ###########################procedure InventoryCreate;
var
i,k, offset : integer;
begin
offset:= 76;
CurrentInvRazmer:=ReadH(4);
for i:=0to InvRazmer doif i < CurrentInvRazmer thenbegin
Inventory[i,0]:=ReadH(i*offset+6); // itemType1
Inventory[i,1]:=ReadD(i*offset+8); // ObjectId
Inventory[i,2]:=ReadD(i*offset+12); // ItemID
Inventory[i,3]:=ReadD(i*offset+20); // count
Inventory[i,4]:=ReadH(i*offset+24); // itemType2
Inventory[i,5]:=ReadH(i*offset+26); // CustType1
Inventory[i,6]:=ReadH(i*offset+28); // isEquipped
Inventory[i,7]:=ReadD(i*offset+30); // BodyPart
Inventory[i,8]:=ReadH(i*offset+34); // EnchantLevel
Inventory[i,9]:=ReadH(i*offset+36); // CustType2endelsefor k:=0to9do Inventory[i,k]:=0; // забиваем нулямиend;
procedure InventoryUpdate;
var
i,j,k, offset: integer;
begin
offset:= 82;
for j:=0to(ReadH(2)-1)dobegincase pck[j*offset+4] of
#$01: k:=0; // add item, запишет на пустую ячейку
#$02: k:=ReadD(j*offset+8); // mod item
#$03: begin// remove item, обнулит ячейки удаленного предмета
k:=ReadD(j*offset+8);
for i:=0to InvRazmer doif(Inventory[i,1]=k)thenbeginfor k:=0to9do Inventory[i,k]:=0;
exit;
end;
end;
end;
for i:=0to InvRazmer doif(Inventory[i,1]=k)thenbegin
Inventory[i,0]:=ReadH(j*offset+6); // itemType1
Inventory[i,1]:=ReadD(j*offset+8); // ObjectId
Inventory[i,2]:=ReadD(j*offset+12); // ItemID
Inventory[i,3]:=ReadD(j*offset+20); // count
Inventory[i,4]:=ReadH(j*offset+24); // itemType2
Inventory[i,5]:=ReadH(j*offset+26); // CustType1
Inventory[i,6]:=ReadH(j*offset+28); // isEquipped
Inventory[i,7]:=ReadD(j*offset+30); // BodyPart
Inventory[i,8]:=ReadH(j*offset+34); // EnchantLevel
Inventory[i,9]:=ReadH(j*offset+36); // CustType2break;
end;
end;
//sendmsg('--доб/изм OID='+inttostr(Inventory[i,1])+' ID='+inttostr(Inventory[i,2]));end;
function GetInv(obj,up,down:integer): integer; // up и down не проверяютсяvar// 0-itemType1, 1-ObjectId, 2-ItemID, 3-count, 4-itemType2, 5-CustType1, 6-isEquipped, 7-BodyPart, 8-EnchantLevel, 9-CustType2
i: integer;
beginfor i:=0to CurrentInvRazmer doif(Inventory[i,up]=obj)thenbegin
Result:=Inventory[i,down];
exit;
end;
Result:=-1;
end;
{procedure UseItemID(ItemID:integer); //Использовать предмет с заданным ItemID
var
ObjItemID : integer;
begin
ObjItemID:= GetInv(ItemID,2,1);
if ObjItemID = -1 then exit;
buf:=#$14;
WriteD(ObjItemID);
WriteD(0);
SendToServerEx(NickName);;
end;}procedure UseItemObjID(ItemObjID:integer); //Использовать предмет с заданным ItemObjIDbegin
buf:=#$19;
WriteD(ItemObjID);
WriteD(0);
SendToServerEx(NickName);;
end;
//############################################################################################function CorrectCoord(Coord: integer): integer;
{коррекция значений координат}begin
result:=Coord;
if result > 2147483648then result:=result-4294967296;
end;
//..............................................................................//function rastoyanie(NpcX, NpcY, NpcZ : integer) : longint; // вычисление растояния между 2 точкамиfunction rastoyanie(NpcX, NpcY, NpcZ : cardinal) : cardinal; // вычисление растояния между 2 точкамиvar// dx,dy,dz : integer;// summa : longint;
dx,dy,dz : cardinal;
summa : cardinal;
begintry
dx:= NpcX-CenterX;
dy:= NpcY-CenterY;
dz:= NpcZ-CenterZ;
summa:= dx*dx+dy*dy; // мне кажется, что так будет быстрее считатьсяif summa = 0then result:= 0else result:= Round(sqrt(summa)); // обход возможной ошибки, если моб стоит прямо в центре качаifabs(dz) > vertical then result := result + OblastVidimosti; // добавляем коррекцию по вертикалиexcept
sendmsg('Ошибка вычисления расстояния в rastoyanie');
sendmsg('npcx= '+ inttostr(npcx)+', npcy= '+ inttostr(npcy)+', npcz= '+ inttostr(npcz)+', dx= '+ inttostr(dx)+', dy= '+ inttostr(dy)+', dz= '+ inttostr(dz));
sendmsg('summa= '+ inttostr(summa));
result := OblastVidimosti;
end;
end;
function RastoyanieToMe(NpcX, NpcY : integer) : integer; // вычисление растояния между 2 точкамиvar
dx,dy, summa : integer;
begintry
dx:= NpcX-MyX;
dy:= NpcY-MyY;
summa:= dx*dx+dy*dy; // мне кажется, что так будет быстрее считатьсяif summa = 0then result:= 0else result:= Round(sqrt(summa)); // обход возможной ошибки, если моб стоит прямо в центре качаexcept
sendmsg('Ошибка вычисления расстояния в RastoyanieToMe');
sendmsg('npcx= '+ inttostr(npcx)+', npcy= '+ inttostr(npcy)+', myx= '+ inttostr(myx)+', myy= '+ inttostr(myy));
sendmsg('summa= '+ inttostr(summa));
result := OblastVidimosti;
end;
end;
procedure AddDroppedItem (ObjID, ItemID, X, Y, Z: integer); // Процедура добавляет дропнутую вещь в БДbeginif ItemsLastIndex = maximumItems then ItemsLastIndex:=0; // обход переполнения базы предметовinc(ItemsLastIndex);
Items_ObjectID[ItemsLastIndex]:= ObjID;
Items_ItemID[ItemsLastIndex]:= ItemID;
Items_XYZ[ItemsLastIndex, OX]:= X;
Items_XYZ[ItemsLastIndex, OY]:= Y;
Items_XYZ[ItemsLastIndex, OZ]:= Z;
// log.Lines.Add('Вещь добавлена, индекс в БД: '+ inttostr(ItemsLastIndex));end;
procedure AddtoDB (id, TypeID, x,y,z : integer; IsAttackable : boolean); // Процедура добавляет в БД нового мобаvar
dist: integer;
beginif(not initmode)then dist:= rastoyanie (x,y,z)else dist := OblastVidimosti; // вычиляем расстояние от центра кача до мобаif dist > (OblastVidimosti)thenexit; // если моб слишко далеко то ничего не делаемinc(MobsLastIndex); // увеличиваем размер БД// log.Lines.Add('Моб добавлен, индекс в БД:'+ inttostr(MobsLastIndex));// if agression then log.Lines.Add('На нас напала какая-то вражина.');
MobsObjID[MobsLastIndex]:= id; // Записываем моба
MobsNpcTypeID[MobsLastIndex]:= TypeID;
MobsDist[MobsLastIndex]:= dist;
MobsXYZ[MobsLastIndex, OX]:= x;
MobsXYZ[MobsLastIndex, OY]:= y;
MobsXYZ[MobsLastIndex, OZ]:= z;
MobsIsAttackable[MobsLastIndex]:= IsAttackable;
MobsAgression[MobsLastIndex]:= false;
MobsUpdated[MobsLastIndex]:= true;
end;
procedure UpdateDB (i, x,y,z : integer; agression : boolean); // Процедура обновляет данные в БД по мобуvar
dist: integer;
beginif(not initmode)then dist:= rastoyanie (x,y,z)else dist := OblastVidimosti;
if dist > (OblastVidimosti)then DelDBItem(i)else// перепроверяем расстояние до моба, если он вышел за границу кача, то удаляем егоbegin// log.Lines.Add('Моб обновлен, индекс в БД:'+ inttostr(i));// if agression then log.Lines.Add('На нас напала какая-то вражина.');
MobsDist[i]:= dist; // записываем данные
MobsXYZ[i, OX]:= x;
MobsXYZ[i, OY]:= y;
MobsXYZ[i, OZ]:= z;
if(not MobsAgression[i])then MobsAgression[i]:= agression;
MobsUpdated[i]:= true;
end;
end;
procedure DelDBItem (i: integer); // процедура удалаяет моба из БДbegin// log.Lines.Add('Моб удален, индекс в БД: '+ inttostr(i));
MobsObjID[i]:= 0;
MobsNpcTypeID[i]:= 0;
MobsDist[i]:= 0;
MobsXYZ[i, OX]:= 0;
MobsXYZ[i, OY]:= 0;
MobsXYZ[i, OZ]:= 0;
MobsAgression[i]:= false;
MobsIsAttackable[i]:= false;
MobsUpdated[i]:= false;
if i < MobsLastIndex then// если надо, производим сдвиг данных в массивахbegin
MobsObjID[i]:= MobsObjID[MobsLastIndex];
MobsNpcTypeID[i]:= MobsNpcTypeID[MobsLastIndex];
MobsDist[i]:= MobsDist[MobsLastIndex];
MobsXYZ[i, OX]:= MobsXYZ[MobsLastIndex, OX];
MobsXYZ[i, OY]:= MobsXYZ[MobsLastIndex, OY];
MobsXYZ[i, OZ]:= MobsXYZ[MobsLastIndex, OZ];
MobsIsAttackable[i]:= MobsIsAttackable[MobsLastIndex];
MobsAgression[i]:= MobsAgression[MobsLastIndex];
MobsUpdated[i]:= MobsUpdated[MobsLastIndex];
end;
dec(MobsLastIndex); // уменьшаем размер БДend;
procedure DelDroppedItem (i: integer); // процедура удалаяет вещь из БДbegin
Items_ObjectID[i]:= 0;
Items_ItemID[i]:= 0;
Items_XYZ[i, OX]:= 0;
Items_XYZ[i, OY]:= 0;
Items_XYZ[i, OZ]:= 0;
// log.Lines.Add('Вещь удалена, индекс в БД: '+ inttostr(i));if i < ItemsLastIndex thenbegin
Items_ObjectID[i]:= Items_ObjectID[ItemsLastIndex];
Items_ItemID[i] := Items_ItemID[ItemsLastIndex];
Items_XYZ[i, OX]:= Items_XYZ[ItemsLastIndex, OX];
Items_XYZ[i, OY]:= Items_XYZ[ItemsLastIndex, OY];
Items_XYZ[i, OZ]:= Items_XYZ[ItemsLastIndex, OZ];
end;
dec(ItemsLastIndex);
end;
function CheckItems (id: integer) : integer; // функция проверяет наличие заданной вещи в БДvar
i: integer;
begin
result:= 0;
for i:=1to ItemsLastIndex doif Items_ObjectID[i] = id thenbegin
result:=i; // И возвращаем его индекс по БДbreak;
end;
end;
function TestPovtor (id: integer) : integer; // функция проверяет наличие заданного моба в БДvar
i: integer;
begin
result:=0;
for i:=1to MobsLastIndex doif MobsObjID[i] = id then// Ищем нужный ID в нашей БДbegin
result:=i; // И возвращаем его индекс по БДbreak;
end;
end;
function InMobsList (NpcTypeID: integer) : boolean; // функция проверяет наличие заданного моба в списке на атакуvar
i: integer;
begin
result:= false;
for i:=1to NpcTypeID_List_Count doif NpcTypeID_List[i] = NpcTypeID then result:= true; // проверяем по спискуend;
procedure SendMsg_to_CL(msg:string); // отправка системных сообщений клиентуbegin
buf:=#$4A;
WriteD(0);
WriteD(10);
WriteS('');
WriteS(msg);
SendToClientEx(NickName);
end;
procedure OnTimerCheckDB (Sender: TObject);
var
i: integer;
begin
i:=0;
while i <= MobsLastIndex dobegininc(i);
if MobsUpdated[i] then MobsUpdated[i]:=falseelsebegin
DelDBItem(i);
dec(i);
end;
end;
end;
procedure PrepareDB;
var
i: integer;
//dist : double;begin
i:=0;
while i <= MobsLastIndex dobegininc(i);
if InMobsList(MobsNpcTypeID[i])then MobsIsAttackable[i]:= true;
MobsDist[i] := rastoyanie (MobsXYZ[i, OX], MobsXYZ[i, OY], MobsXYZ[i, OZ]);
//if MobsDist[i] >= OblastVidimosti then//begin// DelDBItem(i);// dec(i);//end;end;
end;
function ValidateData : boolean; // функция проверки правильности задания всех параметров для начала качаbeginif(MyX <> 0)and(MyY <> 0)and(MyZ <> 0)and(MyID <> 0)and(MyHP > 0)and(MyMaxHP > 0)and// верификация всех данных, чтобы потом в боевом режиме(MyMP > 0)and(MyMaxMP > 0)and{(MyCP <> 0) and (MyMaxCP <> 0) and }// не перепроверять все данные по 100 раз(CenterX <> 0)and(CenterY <> 0)and(CenterZ <> 0)and(Radius > 0)and(NpcTypeID_List_Count > 0)thenbegin
PrepareDB;
SendMsg_to_CL('Все начальные параметры заданы и проверены!');
log.Lines.Add('Все начальные параметры заданы и проверены!');
result:= true;
endelsebegin
SendMsg_to_CL('Ошибка задания начальных параметров!');
log.Lines.Add('Ошибка задания начальных параметров!');
result:= false;
end;
end;
procedure UserCommands; // комманды пользователяvar
i: integer;
begin// если комманда обработана удачно, то в чат сообщение не попадет, а будет выдано системное сообщение прямо в клиентexit; // нам не надо обрабатывать при работе совместно с WalkerScriptRunnerif InitMode thencase(ReadS(2))of'pos' : if MyX <> 0then// центр качаbegin
CenterX:= MyX;
CenterY:= MyY;
CenterZ:= MyZ;
CenterFixed:= true;
SendMsg_to_CL('Центр кача задан успешно!');
log.Lines.Add('Центр кача задан успешно!');
pck:='';
end;
'dist' : if(CenterX <> 0)and(MyX <> 0)then// радиус качаbegin
CenterFixed:= true;
Radius:= rastoyanie (MyX,MyY,MyZ);
SendMsg_to_CL('Радиус кача задан успешно');
SendMsg_to_CL('R= '+ inttostr(Radius));
log.Lines.Add('Радиус кача задан успешно, R = '+ inttostr(Radius));
pck:='';
end;
'reset': begin// сброс параметров
ClearDB;
SendMsg_to_CL('БД очищена, введите заново все параметры');
log.Lines.Add('БД очищена!');
pck:='';
end;
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10' : // задаем мобовbeginif(NpcTypeID_List_Count+1) <> strtoint(ReadS(2))thenbegin
SendMsg_to_CL('Добавлять мобов можно тока по очереди 1, 2, 3...');
pck:='';
exit;
end;
NpcTypeID_CurrentMob:= strtoint(ReadS(2));
SendMsg_to_CL('Добавляем моба № '+ ReadS(2));
pck:='';
i:= TestPovtor (TargetID);
if i > 0thenbegin
NpcTypeID_List[NpcTypeID_CurrentMob]:= MobsNpcTypeID[i];
SendMsg_to_CL('Моб №' + inttostr(NpcTypeID_CurrentMob) + 'тип: '+inttostr(MobsNpcTypeID[i])+' добавлен в базу');
log.Lines.Add('Моб №' + inttostr(NpcTypeID_CurrentMob) + 'тип: '+inttostr(MobsNpcTypeID[i])+' добавлен в базу');
inc(NpcTypeID_List_Count);
NpcTypeID_CurrentMob:= 0;
TargetID:= 0;
end;
end;
'start': begin// собственно запуск бота
pck:='';
StartGame;
end;
end;
if(not InitMode)thencase(ReadS(2))of'pause' : begin
pck:='';
PauseGame;
end;
'stop' : begin
pck:='';
StopGame;
end;
end;
end;
{
procedure PauseGame;
begin
TimerCombat.enabled:= not(TimerCombat.enabled);
TimerBafBanka.enabled:= not(TimerBafBanka.enabled);
if TimerCombat.enabled then
begin
btnPause.Caption:='Pause';
TimerBafBanka.interval:= 15000;
SendMsg('Искуственный интеллект запущен!');
SendMsg_to_CL('Искуственный интеллект запущен!');
log.Lines.Add('Искуственный интеллект запущен!');
end
else
begin
btnPause.Caption:='Repeat';
SendMsg('Искуственный интеллект приостановлен.');
SendMsg_to_CL('Искуственный интеллект приостановлен.');
log.Lines.Add('Искуственный интеллект приостановлен.');
MoveTo(centerX,centerY,centerZ);
end;
end;
}procedure PauseGame;
begin
TimerCombat.enabled:= false;
TimerBafBanka.enabled:= false;
btnPause.Caption:='Repeat';
SendMsg('Искуственный интеллект приостановлен.');
SendMsg_to_CL('Искуственный интеллект приостановлен.');
log.Lines.Add('Искуственный интеллект приостановлен.');
//MoveTo(centerX,centerY,centerZ);end;
procedure ResumeGame;
begin
TimerCombat.enabled:= true;
TimerBafBanka.enabled:= true;
btnPause.Caption:='Pause';
TimerBafBanka.interval:= 15000;
SendMsg('Искуственный интеллект запущен!');
SendMsg_to_CL('Искуственный интеллект запущен!');
log.Lines.Add('Искуственный интеллект запущен!');
end;
procedure StartGame;
beginif ValidateData and InitMode thenbegin
CenterFixed:= true;
SendMsg('ЗАПУСКАЕМ ИСКУСТВЕННЫЙ ИНТЕЛЛЕКТ!');
SendMsg_to_CL('ЗАПУСКАЕМ ИСКУСТВЕННЫЙ ИНТЕЛЛЕКТ!');
log.Lines.Add('ЗАПУСКАЕМ ИСКУСТВЕННЫЙ ИНТЕЛЛЕКТ!');
TargetID:=0;
InitMode:= false;
TimerCombat.enabled:=true; // запускаем таймер
TimerBafBanka.interval:= 15000;
TimerBafBanka.enabled:= true;
btnStart.enabled:= false;
btnStop.enabled:= true;
btnPause.enabled:= true;
endelsebegin
SendMsg('Еще не все параметры заданы. Проверьте параметры...');
SendMsg_to_CL('Еще не все параметры заданы. Проверьте параметры...');
log.Lines.Add('Еще не все параметры заданы. Проверьте параметры...');
end;
end;
procedure StopGame; // остановка качаbegin
TimerCombat.enabled:= false;
InitMode:= true;
TimerPickUp.enabled:=false;
TimerBafBanka.enabled:=false;
TargetID:= 0;
ClearDB;
SendMsg('Искуственный интелект остановлен.');
SendMsg_to_CL('Искуственный интелект остановлен.');
log.Lines.Add('Искуственный интелект остановлен.');
btnStart.enabled:= true;
btnStop.enabled:= false;
btnPause.Caption:= 'Pause';
btnPause.enabled:=false;
end;
function GetMinDistID : integer; // функция поиска ближайшего моба в БДvar
i, Dist, MinDist : integer;
begin
result:= 0;
if MobsLastIndex = 0thenexit;
MinDist:=RastoyanieToMe(MobsXYZ[1,OX], MobsXYZ[1,OY]);
for i:=1to MobsLastIndex doif(MobsDist[i] <= Radius)and(MobsIsAttackable[i])thenbegin
Dist:= RastoyanieToMe(MobsXYZ[i,OX], MobsXYZ[i,OY]);
if Dist <= MinDist thenbegin// Dist:= MinDist;
MinDist:= Dist;
result:= i;
end; // если нашли хоть одного моба или несколько, товозращаем индекс ближайшегоend;
end;
function AgroTest : integer; // функция проверяет, атакует ли меня кто-нибудь или нетvar
i: integer;
begin
result:=0;
for i:=1to MobsLastIndex doif MobsAgression[i] then// ищем первого попавшегося моба, который нас атакуетbegin
result:= i; // возвращаем его индекс по БДbreak;
end;
end;
procedure PhisicalAttack; // команда атакиbegin
buf:=#$1F; //action
WriteD(TargetID);
WriteD(MyX);
WriteD(MyY);
WriteD(MyZ);
WriteC(0);
SendToServerEx(NickName);
end;
procedure RequestMagicSkillUse (SkillID: integer); // маг атакаbegin
buf:=#$39;
WriteD(SkillID);
WriteD(0);
WriteC(0);
SendToServerEx(NickName);
end;
procedure InitPickUpMode(mode: boolean);
beginif mode thenbegin
PickUpMode:= true;
TimerCombat.enabled:= false;
TimerPickUp.enabled:= true;
endelsebegin
PickUpMode:= false;
TimerCombat.enabled:= true;
TimerPickUp.enabled:= false;
end;
end;
procedure OnTimerBafBanka (Sender: TObject); // таймер питья баф-банокbeginif(Bottle_1_Count > 0)and(Bottle_1_ObjID > 0)thenbegin
UseItemObjID(Bottle_1_ObjID);
dec(Bottle_1_Count);
end;
if(Bottle_2_Count > 0)and(Bottle_2_ObjID > 0)thenbegin
UseItemObjID(Bottle_2_ObjID);
dec(Bottle_2_Count);
end;
TimerBafBanka.interval:= Bottle_Interval*60*1000;
end;
procedure OnTimerPickUp (Sender: TObject); // таймер поднятия дропаvar
povtor: integer;
beginif(AgroTest > 0)thenbegin
TargetID:= 0;
InitPickUpMode(false);
exit;
end;
if(ItemsLastIndex > 0)and(TargetID = 0)thenbegin
TargetID:= Items_ObjectID[ItemsLastIndex];
AttackCycle:= 0;
PhisicalAttack;
exit;
end;
if(ItemsLastIndex > 0)and(TargetID > 0)thenbegininc(AttackCycle);
if((AttackCycle mod5) = 0)then PhisicalAttack;
if AttackCycle > Calculated_PickUpTime thenbegin
Povtor:= CheckItems(TargetID);
if Povtor <> 0then DelDroppedItem(Povtor);
TargetID:= 0;
end;
exit;
end;
if ItemsLastIndex = 0then InitPickUpMode(false);
end;
procedure OnTimerCombat (Sender: TObject); // боевой таймер, вся логика поведения бота находится именно здесь!!!var
Agro, MinDistID, i: integer;
beginif TargetID > 0then// если в прицеле есть моб, тоbegin// валим вражинуif((AttackCycle mod5) = 0)thenbeginif Spoil and(not Spoiled)then RequestMagicSkillUse(254)// заспойлим моба если опция включенаelse PhisicalAttack;
end;
inc(AttackCycle);
if AttackCycle > Calculated_AttackTime then// если валим моба больше минуты, значит это баг...begin
i:= TestPovtor(TargetID);
if i > 0thenbegin
MobsIsAttackable[i]:= false; //DelDBItem(i);
MobsAgression[i]:= false;
end;
TargetID:= 0;
end;
exit;
end;
Agro:= AgroTest;
if Agro > 0then// если нас кто-то атакует, то его и выбираемbegin
TargetID:= MobsObjID[Agro];
PhisicalAttack; // берем вражину в таргет
AttackCycle:= 0;
exit;
end;
if ItemsLastIndex > 0then// если чего-то валяется на земле, тоbegin
InitPickUpMode(true); // надо дроп подбиратьexit;
end;
MinDistID:= GetMinDistID; // запускаем алгоритм выбора целиif MinDistID > 0then// иначе ищем ближайшегоbegin
TargetID:= MobsObjID[MinDistID];
PhisicalAttack; // берем вражину в таргет
AttackCycle:= 0;
exit;
end;
if(abs(MyX-CenterX) > 20)and(abs(MyY-CenterY) > 20)then MoveTo (CenterX, CenterY, CenterZ);
end;
procedure MoveTo(TargetX,TargetY,TargetZ:integer); //Идти в точку с координатами x,y,zbegin
buf:=#$0F; //01=MoveBackwardToLocation:d(targetX)d(targetY)d(targetZ)d(originX)d(originY)d(originZ)d(moveByMouse)
WriteD(targetx); //куда
WriteD(targety);
WriteD(targetz);
WriteD(MyX); //откуда
WriteD(MyY);
WriteD(MyZ);
WriteD(1); //используем 1-мышь 0-клавиатура
SendToServerEx(NickName);
end;
procedure OnTimerForm (Sender: TObject); // таймер обновления данных в формеvar
i: integer;
begin
textMyID.text:= inttostr(MyID); // обновляем данные в окне
textX.text:= inttostr(MyX);
textY.text:= inttostr(MyY);
textZ.text:= inttostr(MyZ);
textMyHP.text:= inttostr(MyHP);
textMyMaxHP.text:= inttostr(MyMaxHP);
textMyMP.text:= inttostr(MyMP);
textMyMaxMP.text:= inttostr(MyMaxMP);
// textMyCP.text:= inttostr(MyCP);// textMyMaxCP.text:= inttostr(MyMaxCP);
textCenterX.text:= inttostr(CenterX);
textCenterY.text:= inttostr(CenterY);
textCenterZ.text:= inttostr(CenterZ);
textRadius.text:= inttostr(Radius);
textTargetID.text:= inttostr(TargetID);
textAttackCycle.text:= inttostr(AttackCycle);
textMobsLastIndex.text:= inttostr(MobsLastIndex);
textMobsKilled.text:= inttostr(MobsKilled);
textHPBottleCount.text:= inttostr(HPBottleCount);
textBottle_1_Count.text:= inttostr(Bottle_1_Count);
textBottle_2_Count.text:= inttostr(Bottle_2_Count);
MobsDBscreen.lines.Clear;
for i:=1to MobsLastIndex do// выводим БД мобовbeginif(TargetID = MobsObjID[i])and MobsAgression[i] thenbegin
MobsDBscreen.lines.add('<> '+inttostr(i)+' МобID: '+inttostr(MobsObjID[i])+' ТипID: '+inttostr(MobsNpcTypeID[i])+' дистанция '+inttostr(MobsDist[i]));
continue;
end;
if TargetID = MobsObjID[i] thenbegin
MobsDBscreen.lines.add('-> '+inttostr(i)+' МобID: '+inttostr(MobsObjID[i])+' ТипID: '+inttostr(MobsNpcTypeID[i])+' дистанция '+inttostr(MobsDist[i]));
continue;
end;
if cbFilterRadius.checkedthenif MobsDist[i] <= Radius thenbegin
MobsDBscreen.lines.add('-- '+inttostr(i)+' МобID: '+inttostr(MobsObjID[i])+' ТипID: '+inttostr(MobsNpcTypeID[i])+' дистанция '+inttostr(MobsDist[i]));
continue;
end;
if MobsAgression[i] thenbegin
MobsDBscreen.lines.add('<- '+inttostr(i)+' МобID: '+inttostr(MobsObjID[i])+' ТипID: '+inttostr(MobsNpcTypeID[i])+' дистанция '+inttostr(MobsDist[i]));
continue;
end;
if(not cbFilterRadius.checked)then MobsDBscreen.lines.add('-- '+inttostr(i)+' МобID: '+inttostr(MobsObjID[i])+' ТипID: '+inttostr(MobsNpcTypeID[i])+' дистанция '+inttostr(MobsDist[i]));
end;
ItemsDBscreen.lines.Clear; // выводим БД дропаfor i:=1to ItemsLastIndex do ItemsDBscreen.lines.add('индекс в БД: '+inttostr(i)+' объект ID: '+inttostr(Items_ObjectID[i])+' ID вещи: '+inttostr(Items_ItemID[i]));
if MobsLastIndex = 0then MobsDBscreen.lines.add('Мобы');
if ItemsLastIndex= 0then ItemsDBscreen.lines.add('Дроп');
end;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
begin//Action:=caNone;end;
procedure CreateLabel (text: string); // процедура автоматизирует создание текстовых меток в формеvar
l: TLabel;
begin
l:= TLabel.Create(panel);
l.caption:=text;
l.parent:=panel;
l.left:=10;
l.top:=15+20*frmParamIndex;
inc(frmParamIndex);
end;
function CreateTextBox (text:string) :TEdit; // функция автоматизирует создание текстовых полей для вывода данных в формеvar
e: TEdit;
begin
e:= TEdit.Create(panel);
e.text:=text;
e.parent:=panel;
e.left:=80;
e.top:=10+20*frmParamIndex;
e.width:= 95;
e.ReadOnly:=true;
result:= e;
end;
procedure Free; //Вызывается при выключении скриптаbegin
ClearDB;
TimerForm.free;
TimerCombat.free;
TimerPickUp.free;
TimerBafBanka.free;
TimerCheckDB.free;
MobsDBscreen.free;
ItemsDBscreen.free;
log.free;
frm.free;
end;
procedure UserInfo; //обновление данных о себеvar
i:word;
beginif InitMode then MyID:=ReadD(18);
MyX:=ReadD(2);
MyY:=ReadD(6);
MyZ:=ReadD(10);
i:=22;
ReadS(i);
i:=i+48;
MyMaxHP:=ReadD(i);
MyHP:=ReadD(i);
MyMaxMP:=ReadD(i); //чисто информативно
MyMP:=ReadD(i);
// i:=i+363; //пока не используется// MyMaxCP:=ReadD(i);// MyCP:=ReadD(i);end;
procedure StatusUpdate; //обновление данных о себеvar
i:integer;
beginfor i:=0to ReadD(6)-1docase pck[i*8+10] of
#$09: MyHP:=ReadD(i*8+14);
#$0A: MyMaxHP:=ReadD(i*8+14);
#$0B: MyMP:=ReadD(i*8+14);
#$0C: MyMaxMP:=ReadD(i*8+14);
// #$21: MyCP:=ReadD(i*8+14); //пока не используется// #$22: MyMaxCP:=ReadD(i*8+14);end;
if MyMaxHP > 0then HPlevelProcent:= Round((MyMaxHP/100)*HPLevel);
end;
procedure DrinkBottle; //пъем бутылки и следим за их количествомbeginif(HPBottleCount > 0)and(HPBottleObjID > 0)thenbegin
UseItemObjID(HPBottleObjID);
dec(HPBottleCount);
end;
end;
procedure MoveToLocation01;
var
i: integer;
begin
i:= TestPovtor(ReadD(2));
if i > 0then updateDB(i, ReadD(06), ReadD(10), ReadD(14), false);
end;
procedure NpcInfo16;
var
i: integer;
begin
i:= TestPovtor(ReadD(2));
if i = 0then AddtoDB (ReadD(2), ReadD(6)-kID, ReadD(14), ReadD(18), ReadD(22), InMobsList(ReadD(6)-kID))else UpdateDB(i, ReadD(14), ReadD(18), ReadD(22), false);
end;
procedure Attack05;
var
i: integer;
begin
i:= TestPovtor(ReadD(2));
if i = 0then log.Lines.Add('Глюк, моб не найден, или нас утакует другой игрок!!!')else UpdateDB(i, ReadD(15), ReadD(19), ReadD(23), true);
end;
procedure Die06;
var
i: integer;
begin
i:= TestPovtor(ReadD(2));
if i > 0thenbegin
MobsIsAttackable[i]:= false;
MobsAgression[i]:= false;
if MobsObjID[i] = TargetID thenbegin
TimerCombat.enabled:= false;
if Spoil and Spoiled thenif ReadD(22) = 1then RequestMagicSkillUse(42);
LastKilledMobObjID:=TargetID;
inc(MobsKilled); // подводим статистику
Spoiled:= false;
TargetID:= 0;
TimerCombat.enabled:= true;
end;
end;
end;
procedure DeleteObject12;
var
i: integer;
beginif LastKilledMobObjID = ReadD(2)thenbegin
LastKilledMobObjID:= 0;
i:= TestPovtor(ReadD(2));
if i > 0then DelDBItem(i);
exit;
end;
i:= CheckItems(ReadD(2));
if i > 0thenbeginif Items_ObjectID[i] = TargetID then TargetID:= 0;
DelDroppedItem(i);
exit;
end;
i:= TestPovtor(ReadD(2));
if i > 0thenbeginif TargetID = ReadD(2)then TargetID:= 0;
DelDBItem(i);
exit;
end;
end;
beginif pck = ''thenexit;
if(ConnectName = NickName)and FromServer then//разбор пакетов от сервераcase pck[1] of
#$01: MoveToLocation01; //MoveToLocation:h(ObjectID)d(CurX)d(CurY)d(CurZ)d(DestX)d(DestY)d(DestZ)//#$2F: MoveToLocation01; //MoveToLocation:h(ObjectID)d(CurX)d(CurY)d(CurZ)d(DestX)d(DestY)d(DestZ)// #$03: ; //CharInfo:d(X)d(Y)d(Z)-(4)h(ObjectID)s(Name)d(Race)d(Sex)d(ClassID)-(4)i(Head)i(RHand)i(LHand)i(Gloves)i(Chest)i(Legs)i(Feet)i(Back)i(LRHand)i(Hair)d(PvPFlag)d(Carma)d(MSpeed)d(PSpeed)d(PvpFlag)d(Karma)d(RunSpeed)d(WalkSpeed)d(SwimRunSpeed)d(SwimWalkSpeed)d(FlRunSpeed)d(FlWalkSpeed)d(FlyRunSpeed)d(FlyWalkSpeed)f(MovementSpeedMultiplier)f(AttackSpeedMultiplier)f(CollisionRadius)f(CollisionHeight)d(HairStyle)d(HairColor)d(Face)d(AccessLevel)s(Title)d(ClanId)d(ClanCrestId)d(AllyId)d(AllyCrestId)d(SiegeFlags)b(Sitting)b(Running)b(InCombat)b(AlikeDead)b(Invisible)b(MountType)b(PrivateStoreType)
#$04: if ReadS(22) = NickName then//UserInfo:d(X)d(Y)d(Z)d(Heading)h(ObjectID)s(Name)d(Race)d(Sex)d(ClassID)d(Level)d(Exp)d(STR)d(DEX)d(CON)d(INT)d(WIT)d(MEN)d(MaxHP)d(CurrentHP)d(MaxMP)d(CurrentMP)d(SP)d(CurrentLoad)d(MaxLoad)d(Unknown)d(Under)d(REar)d(LEar)d(Neck)d(RFinger)d(LFinger)d(Head)d(RHand)d(LHand)d(Gloves)d(Chest)d(Legs)d(Feet)d(Back)d(LRHand)d(Hair)i(Under)i(REar)i(LEar)i(Neck)i(RFinger)i(LFinger)i(Head)i(RHand)i(LHand)i(Gloves)i(Chest)i(Legs)i(Feet)i(Back)i(LRHand)i(Hair)d(PAtk)d(PAtkSpd)d(PDef)d(EvasionRate)d(Accuracy)d(CritikalHit)d(MAtk)d(MAtkSpd)d(PAtkSpd)d(MDef)d(PvpFlag)d(Karma)d(RunSpeed)d(WalkSpeed)d(SwimRunSpeed)d(SwimWalkSpeed)d(FlRunSpeed)d(FlWalkSpeed)d(FlyRunSpeed)d(FlyWalkSpeed)f(MovementSpeedMultiplier)f(AttackSpeedMultiplier)f(CollisionRadius)f(CollisionHeight)d(HairStyle)d(HairColor)d(Face)d(AccessLevel)s(Title)d(ClanId)d(ClanCrestId)d(AllyId)d(AllyCrestId)d(IsClanLeader)b(MountType)b(PrivateStoreType)b(DwarvenCraft)d(PkKills)d(PvpKills)b(Cubics)b(Cubics)b(FindPartyMembers)d(AbnormalEffect)b()d(ClanPrivileges)d()d()d()d()d()d()d()b(RecomLeft)b()b(RecomHave)b()// #$32: if ReadS(22) = NickName then //UserInfo:d(X)d(Y)d(Z)d(Heading)h(ObjectID)s(Name)d(Race)d(Sex)d(ClassID)d(Level)d(Exp)d(STR)d(DEX)d(CON)d(INT)d(WIT)d(MEN)d(MaxHP)d(CurrentHP)d(MaxMP)d(CurrentMP)d(SP)d(CurrentLoad)d(MaxLoad)d(Unknown)d(Under)d(REar)d(LEar)d(Neck)d(RFinger)d(LFinger)d(Head)d(RHand)d(LHand)d(Gloves)d(Chest)d(Legs)d(Feet)d(Back)d(LRHand)d(Hair)i(Under)i(REar)i(LEar)i(Neck)i(RFinger)i(LFinger)i(Head)i(RHand)i(LHand)i(Gloves)i(Chest)i(Legs)i(Feet)i(Back)i(LRHand)i(Hair)d(PAtk)d(PAtkSpd)d(PDef)d(EvasionRate)d(Accuracy)d(CritikalHit)d(MAtk)d(MAtkSpd)d(PAtkSpd)d(MDef)d(PvpFlag)d(Karma)d(RunSpeed)d(WalkSpeed)d(SwimRunSpeed)d(SwimWalkSpeed)d(FlRunSpeed)d(FlWalkSpeed)d(FlyRunSpeed)d(FlyWalkSpeed)f(MovementSpeedMultiplier)f(AttackSpeedMultiplier)f(CollisionRadius)f(CollisionHeight)d(HairStyle)d(HairColor)d(Face)d(AccessLevel)s(Title)d(ClanId)d(ClanCrestId)d(AllyId)d(AllyCrestId)d(IsClanLeader)b(MountType)b(PrivateStoreType)b(DwarvenCraft)d(PkKills)d(PvpKills)b(Cubics)b(Cubics)b(FindPartyMembers)d(AbnormalEffect)b()d(ClanPrivileges)d()d()d()d()d()d()d()b(RecomLeft)b()b(RecomHave)b()begin
UserInfo;
btnInit.enabled:= false;
if MyHP = 0then//Проверка не убили ли нас...begin
SendMsg_to_CL('Нас убили...');
log.Lines.Add('Нас убили...');
StopGame;
end;
end;
#$05: if(ReadD(6) = MyID)and(not InitMode)then Attack05; //05= Attack:d(AttackerID)d(TargetID)d(Damage)b(Flags)d(X)d(Y)d(Z)h(Hits)//#$33: if (ReadD(6) = MyID) and (not InitMode) then Attack05; //05= Attack:d(AttackerID)d(TargetID)d(Damage)b(Flags)d(X)d(Y)d(Z)h(Hits)
#$48: begin; //48= MagicSkillUse:h(CharID)h(targetID)d(skillID)d(skillLvl)d(hitTime)d(reuseDelay)d(X)d(Y)d(Z)w(count)d(d)d(d)d(d)if(ReadD(2) = MyID)and(ReadD (6) = TargetID)and(ReadD(10) = 254)then Spoiled:= true;
end;
#$06: Die06; //Die:d(ChaID)//#$00: Die06; //Die:d(ChaID)
#$0C: if LastKilledMobObjID = ReadD(2)then//DropItem:h(PlayerID)h(ObjectID)i(ItemID)d(X)d(Y)d(Z)d(Stackable)d(Count)//#$16: if LastKilledMobObjID = ReadD(2) then //DropItem:h(PlayerID)h(ObjectID)i(ItemID)d(X)d(Y)d(Z)d(Stackable)d(Count)begin
AddDroppedItem(ReadD(6), ReadD(10), ReadD(14), ReadD(18), ReadD(22));
end;
#$0D: if TargetID = ReadD(6)then//GetItem:d(PlayerID)h(ObjectID)d(X)d(Y)d(Z)//#$17: if TargetID = ReadD(6) then //GetItem:d(PlayerID)h(ObjectID)d(X)d(Y)d(Z)begin
TargetID:= 0;
end;
#$0E: if MyID=ReadD(2)then//StatusUpdate:h(ObjectID)d(Attributes)//#$18: if MyID=ReadD(2) then //StatusUpdate:h(ObjectID)d(Attributes)begin
StatusUpdate;
if(MyHP > 0)and(MyHP < HPlevelProcent)thenif Wait(time1,DrinkDelay)then DrinkBottle; //пъем бутылкиif MyHP > HPlevelProcent then time1:=1;
if MyHP = 0then//Проверка не убили ли нас...begin
SendMsg_to_CL('Нас убили...');
log.Lines.Add('Нас убили...');
StopGame;
end;
end;
#$12: DeleteObject12; //DeleteObject:h(ObjectID)//#$08: DeleteObject12; //DeleteObject:h(ObjectID)//NpcInfo:h(ObjectID)d(NpcTypeID)d(IsAttackable)d(X)d(Y)d(Z)d(Heading)d(Unknown)d(MAtkSpd)d(PAtkSpd)d(RunSpd)d(WalkSpd)d(SwimRunSpd)d(SwimWalkSpd)d(FlRunSpd)d(FlWalkSpd)d(FlyRunSpd)d(FlyWalkSpd)f(ProperMultiplier)f(PAtkSpd)f(CollisionRadius)f(CollisionHeight)d(RHand)d(Unknown)d(LHand)b(Unknown)b(IsRunning)b(IsInCombat)b(IsALikeDead)b(IsSummoned)s(Name)s(Title)
#$16: if(ReadD(10)=1)and(pck[121]=#$00)then NpcInfo16;
//#$0C: if (ReadD(10)=1) and (pck[121]=#$00) then NpcInfo16;
#$1B: begin//#$11: begin
InventoryCreate; //Инвентарьif HPBottleID <> 0thenbegin
HPBottleObjID:= getinv(HPBottleID, 2,1);
HPBottleCount:= getinv(HPBottleID, 2,3);
end;
if Bottle_1_ID <> 0thenbegin
Bottle_1_ObjID:=getinv(Bottle_1_ID, 2,1);
Bottle_1_Count:=getinv(Bottle_1_ID, 2,3);
end;
if Bottle_2_ID <> 0thenbegin
Bottle_2_ObjID:=getinv(Bottle_2_ID, 2,1);
Bottle_2_Count:=getinv(Bottle_2_ID, 2,3);
end;
end;
#$27: begin//#$21: begin
InventoryUpdate;
if HPBottleID <> 0thenbegin
HPBottleObjID:= getinv(HPBottleID, 2,1);
HPBottleCount:= getinv(HPBottleID, 2,3);
end;
if Bottle_1_ID <> 0thenbegin
Bottle_1_ObjID:=getinv(Bottle_1_ID, 2,1);
Bottle_1_Count:=getinv(Bottle_1_ID, 2,3);
end;
if Bottle_2_ID <> 0thenbegin
Bottle_2_ObjID:=getinv(Bottle_2_ID, 2,1);
Bottle_2_Count:=getinv(Bottle_2_ID, 2,3);
end;
end;
#$13: if RestartMode then//CharacterSelectionInfo//#$09: if RestartMode then //CharacterSelectionInfobegin
buf:=#$12; //CharacterSelected
WriteD(CharNumber);
buf:= buf + hstr('00 00 00 00 00 00 00 00 00 00 00 00 00 00');
SendToServerEx(NickName);
RestartMode:= false;
end;
end;
if(ConnectName = NickName)and FromClient then//разбор пакетов от клиентаcase pck[1] of
#$1F: if InitMode then TargetID:= ReadD(2); //Action:h(ObjectID)d(OriginX)d(OriginY)d(OriginZ)b(ActionID)
#$49: UserCommands; //Say2:s(Text)d(Type)s(Target)
#$59: begin//ValidatePosition:d(X)d(Y)d(Z)d(Heading)d(Data)
MyX:= ReadD(2);
MyY:= ReadD(6);
MyZ:= ReadD(10);
if(not CenterFixed)and InitMode thenbegin
CenterX:= MyX;
CenterY:= MyY;
CenterZ:= MyZ;
end;
end;
end;
end.
<<
В папке ./scripts/walkerscript/ должен лежать файл, загружаемый по умолчанию default.sec
delphi Код:
MSG(Скрипт загружаемый по умолчанию, задает атаковать всех, в радиусе 2000)
DELAY(5000)SET(MON,ATTACK,*)SET(RangeType,StartPos,2000)SET(FIGHTSTART)EXIT()
также там должны лежать все остальные скрипты для валкера, которые грузим по LOAD=имя_скрипта_без_расширения
Настраиваем скрипты, как обычно (Name:='имя чара'; ).
Запускаем сначала локомотив (панель можно закрыть), затем эмулятор.
В чате пишем load=test - загружаем файл TEST.SEC, start - начнется исполнение загруженного файла, stop - остановим выполнение загруженного файла.
version 0.11 от 03.06.2009г.
[+] Понимает команды:
LoadItem(ITEMNAME[ID=#],#)
SaveItem(ITEMNAME[ID=#],#)
[+] Ведём базу хранилища[*] Подправил GetBypass, теперь можно писать в команде DLGSEL(), только начало строки. [*] Подправил команду DLGSEL() для того, чтобы она ждала пока чар не подбежит на достаточное расстояние к НПЦ
NLObP, с помощью чего можно осуществить автономное пати на прокачку одного чара. еси неошибаюсь в валкере 10.9.1 нету RequestJoinParty. рас через валкер неполучаеться, пробую валкер+пнх. но т.к. в делпфи полный нуль, то и рыпаться в нужном направлении неполучаеться