Долго мучисля, но все таки научил его спойлить. Выкладываю правленый скрипт бота от Alexus.
Огромное спасибо QaK за оказаную помощь и проявленное терпение=)
delphi Код:
{#############################################################################
Бот - локомотив от Alexus edit by VismuT (Научился спойлить)
версия : 0.3 (бета)
дата: 08.04.08
Описание:
Бот для кача в принципе любого война. По функциональности это еще не Волкер, но скрипт уже довольно умный :)
Охотиться только на заданных мобов.
Возможности:
- запоминает центр и радиус кача
- запоминает список мобов на которых надо охотиться!
- запоминает и затем подбирает дроп, упавший с убитых им мобов
- умеет лечиться банками (тип банки легко настраивается), ведется учет количества оставшихся банок
- управляется из окна чата в самом клиенте
- имеет окно для вывода всех параметров и статистики
- если моб по какой-то причине стал недосягаемым (стоит за деревом), то через 1 мин БОТ переключится на другого моба.
- если бот умирает, то не палимся и прекращаем рыпаться...(скоро появиться возможность логаута)
- работает под С4 и С6
Недостатки: (со временем будет исправлено)
- бот не умеет обходить препядствия
- бот может увязаться за атаковавшим его другим игроком
Инструкция:
1. Запускаем ПакетХак, запускаем игрового клиента
2. Внимательно НАСТРАИВАЕМ параметры в секции настройки, жмем кнопку "Сохранить" и запускаем скрипт.
3. Добираемся до места кача
4. Выбираем в таргет моба на которого хотим охотится и отправляем в общий чат-> 1
5. Валим моба. Если все правильно сделали будет выдано сообщение в чате что моб добавлен.
6. Выбираем следующего моба отправляем в общий чат цифру 2, валим его и т.д. можно выбрать до 10 разных тварей.
7. Становимся в центр кача и отправляем в общий чат-> pos , если все правильно то в чате будет выдано сообщение, что координаты заданы.
8. Бежим к краю радиуса кача и отправляем в общий чат-> dist, в чате вылезет сообщение от системы, что радиус задан.
9. Валим как можно больше мобов в округе, становимся примерно в центр и отправляем в общий чат-> start ,
если все сделано верно, то будет выдано соответствующее сообщение.
10. Если бот поймал моба в таргет и побежал его атаковать, то можно свернуть окно игры
и смотреть на информационное окно скрипта.
#############################################################################}const//################### Сеция настройки бота ############################################
NickName = 'HiliH'; // Твой ник в игре
Interlude = true; // C4 - false, C6 - true;
HPMedium = 40; // Уровень жизни, при котором начинаем глотать банки
HPBottleID = 1061; // ItemID 1060=Lesser Healing Potion; 1061=Healing Potion; 1539=Greater Healing Potion;
Vertical = 300; // Вертикальный радиус кача
InvRazmer = 99; // Размер инвентаря -1 (т.е. если инвентарь 80, то пишем сюда 79)//############# Секция расширенной настройки для опытных ботоводеров :) ###################################
TimerCombat_Interval = 1000; // частота срабатывания таймера атаки в милисекундах, чем меньше это число, тем быстрее бот будет думать.
TimerPickUp_Interval = 750; // частота срабатывания таймера сбора дропа, чем меньше это число, тем быстрее бот будет думать.
TimerForm_Interval = 10000; // частота срабатывания таймера обновления формы, чем меньше это число, тем быстрее будут обновляться данные в форме.
DrinkDelay = 14; // минимальное время задержки повторного питься бутылки (10 - 20 сек)
Attack_Time = 60; // Время на убивание моба в секундах.
PickUp_Time = 30; // Врямя на поднятие дропа в секундах.
OblastVidimosti = 1000; // Приращение к радиусу кача, для мобов которые стоят прямо на границе радиуса (изменять от 100 до 1000)
maximumItems = 100; // Размер базы мобов и дропа//########################################################################################
NpcTypeID_List_Razmer = 10; // Размер списка мобов (не трогать!)
OX = 1; OY = 2; OZ= 3; // Служебные константыvar
InitMode, PickUpMode, SpoilMode : boolean;
//-------------------------------------- БД --------------------------------------------------
MobsID : array [1..maximumItems] ofinteger;
MobsDist : array [1..maximumItems] ofinteger; // Расстояние от центра кача
MobsXYZ : array [1..maximumItems, 1..3] ofinteger; // Координаты
MobsAgression : 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)
HPBottleObjID, HPBottleCount : integer; // ObjID и количество HP-бутылей
TargetID, LastKilledMobObjID : integer; // Текущая цель, последний убитый моб
MobsKilled : integer; // счетчик убитых мобов
Povtor: integer;
CenterX, CenterY, CenterZ : integer; // Центр кача
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;
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, textMobsKilled: TEdit;
//----------------------------------------------------------
TimerForm, TimerCombat, TimerPickUp : TTimer; // таймеры
time1: integer;
Calculated_AttackTime, Calculated_PickUpTime : integer;
AttackCycle: integer; // Цикл атаки//##############################################################################################procedure Init; //Вызывается при включении скриптаbegin
ClearDB;
MyID:= 0; // обнуляем ВСЕ данные
MyX:= 0;
MyY:= 0;
MyZ:= 0;
MyID:= 0;
MyHP:= 0;
MyMaxHP:= 0;
MyMP:= 0;
MyMaxMP:= 0;
MyCP:= 0;
MyMaxCP:= 0;
time1:=1;
SpoilMode:= true;
Calculated_AttackTime:= round(1000 / TimerCombat_Interval * Attack_Time);
Calculated_PickUpTime:= round(1000 / TimerPickUp_Interval * PickUp_Time);
HPlevelProcent:= 0;
HPBottleObjID:= 0;
HPBottleCount:= 0;
InitMode:= true;
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;
frmParamIndex:=0; // создаем контролы в форме
frm:= TForm.Create(nil);
frm.Caption:= 'BOT by Alexus ver: 0.3 beta';
frm.BorderStyle := bsSizeable;
frm.Position := poScreenCenter;
frm.Width:=650;
frm.Height:=700;
MobsDBscreen:=TMemo.Create(frm);
MobsDBscreen.parent:=frm;
MobsDBscreen.ReadOnly:=true;
MobsDBscreen.ScrollBars:=2;
MobsDBscreen.Top:=1;
MobsDBscreen.Width:=460;
MobsDBscreen.Height:=285;
MobsDBscreen.Lines.Add('Мобы');
ItemsDBscreen:=TMemo.Create(frm);
ItemsDBscreen.parent:=frm;
ItemsDBscreen.ReadOnly:=true;
ItemsDBscreen.ScrollBars:=2;
ItemsDBscreen.Top:=286;
ItemsDBscreen.Width:=460;
ItemsDBscreen.Height:=285;
ItemsDBscreen.Lines.Add('Дроп');
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('...');
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('Цикл атаки:');
textMobsKilled:= CreateTextBox('MobsKilled');
CreateLabel('Убито моб:');
frm.Show; // выводим форму на экран
buf:= #$0F; // принудительно вызываем пакеты инвентаря и userinfo
SendToServerEx(NickName);
end;
procedure ClearDB; // Очистка БДvar// Надо все занулять, иначе там хрень всякая вылезает или старые данные
i, n: word;
begin
MobsLastIndex:= 0; // Очищаем переменные
ItemsLastIndex:= 0;
TargetID:= 0;
LastKilledMobObjID:= 0;
MobsKilled:= 0;
Povtor:= 0;
CenterX:= 0;
CenterY:= 0;
CenterZ:= 0;
Radius:= 0;
AttackCycle:= 0;
for i:=1to NpcTypeID_List_Razmer do NpcTypeID_List[i]:= 0;
NpcTypeID_List_Count:= 0;
NpcTypeID_CurrentMob:= 0;
for i:=1to maximumItems do// Очищаем базуbegin
MobsID[i]:= 0;
MobsDist[i]:= 0;
MobsXYZ[i, OX]:= 0;
MobsXYZ[i, OY]:= 0;
MobsXYZ[i, OZ]:= 0;
MobsAgression[i]:= false;
Items_ObjectID[i]:= 0;
Items_ItemID[i]:= 0;
Items_XYZ[i, OX]:= 0;
Items_XYZ[i, OY]:= 0;
Items_XYZ[i, OZ]:= 0;
end;
for i:=0to InvRazmer dofor n:=0to9do Inventory[i, n]:= 0;
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;
beginif interlude then offset:= 8else offset:=0;
for i:=0to InvRazmer doif(i<ReadH(4))thenbegin
Inventory[i,0]:=ReadH(i*(28+offset)+6); // itemType1
Inventory[i,1]:=ReadD(i*(28+offset)+8); // ObjectId
Inventory[i,2]:=ReadD(i*(28+offset)+12); // ItemID
Inventory[i,3]:=ReadD(i*(28+offset)+16); // count
Inventory[i,4]:=ReadH(i*(28+offset)+20); // itemType2
Inventory[i,5]:=ReadH(i*(28+offset)+22); // CustType1
Inventory[i,6]:=ReadH(i*(28+offset)+24); // isEquipped
Inventory[i,7]:=ReadD(i*(28+offset)+26); // BodyPart
Inventory[i,8]:=ReadH(i*(28+offset)+30); // EnchantLevel
Inventory[i,9]:=ReadH(i*(28+offset)+32); // CustType2endelsefor k:=0to9do Inventory[i,k]:=0; // забиваем нулямиend;
procedure InventoryUpdate;
var
i,j,k, offset: integer;
beginif interlude then offset:= 8else offset:=0;
for j:=0to(ReadH(2)-1)dobegincase pck[j*(30+offset)+4] of
#$01: k:=0; // add item, запишет на пустую ячейку
#$02: k:=ReadD(j*(30+offset)+8); // mod item
#$03: begin// remove item, обнулит ячейки удаленного предмета
k:=ReadD(j*(30+offset)+8);
for i:=0to InvRazmer doif(Inventory[i,0]=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*(30+offset)+6); // itemType1
Inventory[i,1]:=ReadD(j*(30+offset)+8); // ObjectId
Inventory[i,2]:=ReadD(j*(30+offset)+12); // ItemID
Inventory[i,3]:=ReadD(j*(30+offset)+16); // count
Inventory[i,4]:=ReadH(j*(30+offset)+20); // itemType2
Inventory[i,5]:=ReadH(j*(30+offset)+22); // CustType1
Inventory[i,6]:=ReadH(j*(30+offset)+24); // isEquipped
Inventory[i,7]:=ReadD(j*(30+offset)+26); // BodyPart
Inventory[i,8]:=ReadH(j*(30+offset)+30); // EnchantLevel
Inventory[i,9]:=ReadH(j*(30+offset)+32); // CustType2break;
end;
end;
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 InvRazmer 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); //Использовать предмет с заданным ItemIDbegin
buf:=#$14;
WriteD(ItemObjID);
WriteD(0);
SendToServerEx(NickName);;
end;
//############################################################################################function rastoyanie(NpcX, NpcY, NpcZ : integer) : integer; // вычисление растояния между 2 точкамиvar
dx,dy,dz, summa : integer;
begin
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 + 5000; // добавляем коррекцию по вертикалиend;
procedure AddDroppedItem (ObjID, ItemID, X, Y, Z: integer); // Процедура добавляет дропнутую вещь в БДbegininc(ItemsLastIndex);
if ItemsLastIndex > maximumItems thenexit;
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, x,y,z : integer; agression : boolean); // Процедура добавляет в БД нового мобаvar
i:integer;
dist: integer;
begin
dist:= rastoyanie (x,y,z); // вычиляем расстояние от центра кача до мобаif dist > (Radius + OblastVidimosti)thenexit; // если моб слишко далеко то ничего не делаемinc(MobsLastIndex); // увеличиваем размер БД
log.Lines.Add('Моб добавлен, индекс в БД: '+ inttostr(i));
if agression then log.Lines.Add('На нас напала какая-то вражина.');
MobsID[MobsLastIndex]:= id; // Записываем моба
MobsDist[MobsLastIndex]:= dist;
MobsXYZ[MobsLastIndex, OX]:= x;
MobsXYZ[MobsLastIndex, OY]:= y;
MobsXYZ[MobsLastIndex, OZ]:= z;
MobsAgression[MobsLastIndex]:= agression;
end;
procedure UpdateDB (i:integer; id, x,y,z : integer; agression : boolean); // Процедура обновляет данные в БД по мобуvar
dist: integer;
beginif(MOBSXYZ[i, OX] = x)and(MOBSXYZ[i, OY] = y)and(MobsAgression[i] = agression)thenexit; // проверяем, а надо ли чего нить менят
dist:= rastoyanie (x,y,z);
if dist > (Radius + 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;
MobsAgression[i]:= agression;
end;
end;
procedure DelDBItem (i: integer); // процедура удалаяет моба из БДvar
n: integer;
begin
log.Lines.Add('Моб удален, индекс в БД: '+ inttostr(i));
MobsID[i]:= 0;
MobsDist[i]:= 0;
MobsXYZ[i, OX]:= 0;
MobsXYZ[i, OY]:= 0;
MobsXYZ[i, OZ]:= 0;
MobsAgression[i]:= false;
if i < MobsLastIndex thenfor n:= i+1to MobsLastIndex do// если надо, производим циклический сдвиг данных в массивахbegin
MobsID[n-1]:= MobsID[n];
MobsDist[n-1]:= MobsDist[n];
MobsXYZ[n-1, OX]:= MobsXYZ[n, OX];
MobsXYZ[n-1, OY]:= MobsXYZ[n, OY];
MobsXYZ[n-1, OZ]:= MobsXYZ[n, OZ];
MobsAgression[n-1]:= MobsAgression[n];
end;
dec(MobsLastIndex); // уменьшаем размер БДend;
procedure DelDroppedItem (i: integer); // процедура удалаяет вещь из БДvar
n: 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 thenfor n:= i+1to ItemsLastIndex dobegin
Items_ObjectID[n-1]:= Items_ObjectID[n];
Items_ItemID[n-1] := Items_ItemID[n];
Items_XYZ[n-1, OX]:= Items_XYZ[n, OX];
Items_XYZ[n-1, OY]:= Items_XYZ[n, OY];
Items_XYZ[n-1, OZ]:= Items_XYZ[n, 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 MobsID[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(msg:string); // отправка системных сообщений клиентуbegin
buf:=#$4A;
WriteD(0);
WriteD(10);
WriteS('');
WriteS(msg);
SendToClientEx(NickName);
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
SendMsg('Все начальные параметры заданы и проверены!');
log.Lines.Add('Все начальные параметры заданы и проверены!');
result:= true;
endelsebegin
SendMsg('Ошибка задания начальных параметров!');
log.Lines.Add('Ошибка задания начальных параметров!');
result:= false;
end;
end;
procedure UserCommandsInitMode; // комманды пользователя для режима настройки ботаbegin// если комманда обработана удачно, то в чат сообщение не попадет, а будет выдано системное сообщение прямо в клиентcase(ReadS(2))of'pos' : if MyX <> 0then// центр качаbegin
CenterX:= MyX;
CenterY:= MyY;
CenterZ:= MyZ;
SendMsg('Центр кача задан успешно!');
log.Lines.Add('Центр кача задан успешно!');
pck:='';
end;
'dist' : if(CenterX <> 0)and(MyX <> 0)then// радиус качаbegin
Radius:= rastoyanie (MyX,MyY,MyZ);
SendMsg('Радиус кача задан успешно');
SendMsg('R= '+ inttostr(Radius));
log.Lines.Add('Радиус кача задан успешно, R = '+ inttostr(Radius));
pck:='';
end;
'reset': begin// сброс параметров
ClearDB;
SendMsg('БД очищена, введите заново все параметры');
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('Добавлять мобов можно тока по очереди 1, 2, 3...');
pck:='';
exit;
end;
NpcTypeID_CurrentMob:= strtoint(ReadS(2));
SendMsg('Добавляем моба № '+ ReadS(2));
pck:='';
end;
'start': begin// собственно запуск ботаif ValidateData and InitMode thenbegin
SendMsg('ЗАПУСКАЕМ ИСУСТВЕННЫЙ ИНТЕЛЛЕКТ!');
log.Lines.Add('ЗАПУСКАЕМ ИСУСТВЕННЫЙ ИНТЕЛЛЕКТ!');
TargetID:=0;
InitMode:= false;
TimerCombat.enabled:=true; // запускаем таймерendelsebegin
SendMsg('Еще не все параметры заданы. Проверьте параметры...');
log.Lines.Add('Еще не все параметры заданы. Проверьте параметры...');
end;
pck:='';
end;
end;
end;
procedure UserCommandsCombatMode; // комманды пользователя для боевого режимаbegin// команды говорят сами за себя)case(ReadS(2))of'pause' : begin
TimerCombat.enabled:= not(TimerCombat.enabled);
pck:='';
if TimerCombat.enabledthenbegin
SendMsg('Искуственный интелект запущен!');
log.Lines.Add('Искуственный интелект запущен!');
endelsebegin
SendMsg('Искуственный интелект приостановлен.');
log.Lines.Add('Искуственный интелект приостановлен.');
endend;
'stop' : begin
pck:='';
SendMsg('Искуственный интелект остановлен.');
log.Lines.Add('Искуственный интелект остановлен.');
StopGame;
end;
end;
end;
procedure StopGame; // остановка качаbegin
TimerCombat.enabled:= false;
// InitMode:= true;
TimerPickUp.enabled:=false;
TargetID:= 0;
ClearDB;
end;
function GetMinDistID : integer; // функция поиска ближайшего моба в БДvar
i, dist : integer;
begin
dist:=10000; // задаем заранее нереальную дистанциюfor i:=1to MobsLastIndex doif(MobsID[i] <> 0)and(MobsDist[i] < dist)thenbegin// фишка в том, что в базе хранятся расстояния не до меня, а до центра кача
dist:= MobsDist[i]; // но в катах то расстояния небольшие
result:= i; // если нашли хоть одного моба или несколько возращаем его индекс ближайшего к центру качаend;
if dist = 10000then result:= 0; // иначе возвращаем 0 (ничего не найдено)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:=#$04; //action
WriteD(TargetID);
WriteD(MyX);
WriteD(MyY);
WriteD(MyZ);
WriteC(0);
SendToServerEx(NickName);
if SpoilMode=truethenbegin
buf:=hstr('2F FE 00 00 00 00 00 00 00 00');
SendToServerEX(NickName);
end;
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 OnTimerPickUp (Sender: TObject); // таймер поднятия дропаvar
povtor: integer;
beginif(AgroTest > 0)and(TargetID = 0)thenbegin
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 = 0)then InitPickUpMode(false);
end;
procedure OnTimerCombat (Sender: TObject); // боевой таймер, вся логика поведения бота находится именно здесь!!!var
Agro, MinDistID: integer;
beginif MobsLastIndex = 0then// если нет мобов в базе, тоbeginif ItemsLastIndex > 0then InitPickUpMode(true); // надо дроп подбиратьexit; // выходим от сюдаend;
if TargetID > 0then// если в прицеле есть моб, тоbegin// валим вражинуif((AttackCycle mod5) = 0)then PhisicalAttack;
inc(AttackCycle);
if AttackCycle > Calculated_AttackTime then// если валим моба больше минуты, значит это баг...begin
Povtor:= TestPovtor(TargetID);
if Povtor <> 0then DelDBItem(Povtor);
TargetID:= 0;
end;
exit;
end;
Agro:= AgroTest; // если есть мобы в базе и нет текущей цели, то
MinDistID:= GetMinDistID; // запускаем алгоритм выбора целиif Agro > 0then// если нас кто-то атакует, то его и выбираемbegin
TargetID:= MobsID[Agro];
PhisicalAttack; // берем вражину в таргет
AttackCycle:= 0;
exit;
end;
if ItemsLastIndex > 0then// если чего-то валяется на земле, тоbegin
InitPickUpMode(true); // надо дроп подбиратьexit;
end;
if MobsDist[MinDistID] <= Radius then// иначе ищем ближайшего #################################################begin
TargetID:= MobsID[MinDistID];
PhisicalAttack; // берем вражину в таргет
AttackCycle:= 0;
exit;
endend;
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);
textMobsKilled.text:= inttostr(MobsKilled);
MobsDBscreen.lines.Clear;
for i:=1to MobsLastIndex do// выводим БД мобовbeginif TargetID = MobsID[i] then MobsDBscreen.lines.add('-> '+inttostr(i)+' МобID: '+inttostr(MobsID[i])+' дистанция '+inttostr(MobsDist[i]))elseif MobsAgression[i] then MobsDBscreen.lines.add('<- '+inttostr(i)+' МобID: '+inttostr(MobsID[i])+' дистанция '+inttostr(MobsDist[i]))else MobsDBscreen.lines.add('-- '+inttostr(i)+' МобID: '+inttostr(MobsID[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]));
end;
procedure CreateLabel (text: string); // процедура автоматизирует создание текстовых меток в формеvar
l: TLabel;
begin
l:= TLabel.Create(panel);
l.caption:=text;
l.parent:=panel;
l.left:=5;
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:=60;
e.top:=10+20*frmParamIndex;
result:= e;
end;
procedure Free; //Вызывается при выключении скриптаbegin
MobsDBscreen.free;
ItemsDBscreen.free;
log.free;
frm.free;
ClearDB;
TimerForm.free;
TimerCombat.free;
TimerPickUp.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);
if interlude then i:=i+48else i:=i+44;
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)*HPMedium);
end;
procedure DrinkBottle; // пьем бутылки и следим за их количествомbeginif HPBottleCount > 0thenbegin
UseItemObjID(HPBottleObjID);
dec(HPBottleCount);
end;
end;
beginif pck = ''thenexit;
if(Connectname=nickname)and FromServer and(pck=HStr('64 64 02 00 00 00 00 00 00'))thenbegin
SpoilMode:=false;
end;
if(ConnectName = NickName)and FromServer and(not InitMode)then// разбор пакетов от сервера в Боевом режимеcase pck[1] of
#$01: begin// MoveToLocation:h(ObjectID)d(CurX)d(CurY)d(CurZ)d(DestX)d(DestY)d(DestZ)
povtor:= TestPovtor(ReadD(2));
if Povtor > 0thenbegin
updateDB(Povtor, ReadD(2), ReadD(18), ReadD(22), ReadD(26), false);
end;
end;
// #$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()begin
UserInfo;
if MyHP = 0then// Проверка не убили ли нас...begin
SendMsg('Нас убили...');
log.Lines.Add('Нас убили...');
StopGame;
end;
end;
#$05{, #$48}: begin// 05= Attack:d(AttackerID)d(TargetID)d(Damage)b(Flags)d(X)d(Y)d(Z)h(Hits)if ReadD(6) = MyID thenbegin
Povtor:= TestPovtor(ReadD(2));// 48= MagicSkillUseif Povtor = 0then AddtoDB (ReadD(2), ReadD(15), ReadD(19), ReadD(23), true)else UpdateDB(Povtor, ReadD(2), ReadD(15), ReadD(19), ReadD(23), true);
end;
end;
#$06: begin// Die:d(ChaID)
povtor:= TestPovtor(ReadD(2));
if Povtor > 0thenbeginif MobsID[povtor] = TargetID thenbegin
LastKilledMobObjID:=TargetID;
inc(MobsKilled); // подводим статистику
SpoilMode:=true;
TargetID:= 0;
buf:=hstr('2F 2A 00 00 00 00 00 00 00 00');
SendToServerEX(NickName);
// споил надо делать тутаend;
DelDBItem(povtor);
end;
end;
#$0C: 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)begin
TargetID:= 0;
end;
#$0E: 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('Нас убили...');
log.Lines.Add('Нас убили...');
StopGame;
end;
end;
#$12: begin// DeleteObject:h(ObjectID)if LastKilledMobObjID = ReadD(2)then LastKilledMobObjID:= 0elsebegin
Povtor:= CheckItems(ReadD(2));
if Povtor <> 0then DelDroppedItem(Povtor);
end;
end;
#$16: begin// 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)if InMobsList(ReadD(6))and(ReadD(10)=1)and(pck[121]=#$00)thenbegin
Povtor:= TestPovtor(ReadD(2));
if Povtor = 0then AddtoDB (ReadD(2), ReadD(14), ReadD(18), ReadD(22), false)else UpdateDB(Povtor, ReadD(2), ReadD(14), ReadD(18), ReadD(22), false);
end;
end;
end;
if(ConnectName = NickName)and FromClient and(not InitMode)then// разбор пакетов от клиента в боевом режимеcase pck[1] of// #$04: {TargetID:= ReadD(2)}; // Action:h(ObjectID)d(OriginX)d(OriginY)d(OriginZ)b(ActionID)
#$38: UserCommandsCombatMode; // Say2:s(Text)d(Type)s(Target)
#$48: begin// ValidatePosition:d(X)d(Y)d(Z)d(Heading)d(Data)
MyX:= ReadD(2);
MyY:= ReadD(6);
MyZ:= ReadD(10);
end;
end;
if(ConnectName = NickName)and FromServer and InitMode then// разбор пакетов от сервера в режиме настройки ботаcase pck[1] of
#$04: if ReadS(22) = NickName then UserInfo; // 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()
#$06: if(ReadD(2) = TargetID)and(NpcTypeID_CurrentMob <> 0)then// Die:d(ChaID)if(NpcTypeID_List[NpcTypeID_CurrentMob] <> 0)thenbegin
SendMsg('Моб №' + inttostr(NpcTypeID_CurrentMob) + ' добавлен в базу');
log.Lines.Add('Моб №' + inttostr(NpcTypeID_CurrentMob) + ' добавлен в базу');
inc(NpcTypeID_List_Count);
NpcTypeID_CurrentMob:= 0;
TargetID:= 0;
end;
#$16: if(ReadD(2) = TargetID)and(NpcTypeID_CurrentMob <> 0)then NpcTypeID_List[NpcTypeID_CurrentMob]:= ReadD(6);//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)
#$1B: begin
InventoryCreate; // Инвентарь
HPBottleObjID:= getinv(HPBottleID, 2,1);
HPBottleCount:= getinv(HPBottleID, 2,3);
end;
#$27: begin
InventoryUpdate;
HPBottleCount:= getinv(HPBottleID, 2,3);
end;
end;
if(ConnectName = NickName)and FromClient and InitMode then// разбор пакетов от клиента в режиме настройки ботаcase pck[1] of
#$04: TargetID:= ReadD(2); // Action:h(ObjectID)d(OriginX)d(OriginY)d(OriginZ)b(ActionID)
#$38: UserCommandsInitMode; // Say2:s(Text)d(Type)s(Target)
#$48: begin// ValidatePosition:d(X)d(Y)d(Z)d(Heading)d(Data)
MyX:= ReadD(2);
MyY:= ReadD(6);
MyZ:= ReadD(10);
end;
end;
end.
Долго мучисля, но все таки научил его спойлить. Выкладываю правленый скрипт бота от Alexus.
Огромное спасибо QaK за оказаную помощь и проявленное терпение=)
Извини VismuT, но твой вариант спойла палевом попахивает. Процедура PhisicalAttack при атаке моба вызывается раз в 1 сек. А после твоих модификаций получается, что сразу за пакетом 04=Action, посылается пакет 2F=RequestMagicSkillUse, и так раз в секунду, пока моба не добьешь. Не думаю что это хорошо...