A
>A1RN1kE<
Original poster
Добрый день!
В этой статье я расскажу о том, как создать самый простой Auto-Potter для Perfect World. Итак, приступим. Для начала опишем функции, которые мы будем использовать:
- FindWindow(ClassName, WindowName: PChar): HWnd; ClassName - Имя класса окна (заканчивающееся пустым символом, nil - если все классы). WindowName - Текстовый заголовок окна или 0, если все окна. С помощью этой функции мы будем искать Handle окна клиента.
- GetWindowThreadProcessId(hWnd: HWND; lpdwProcessId: Pointer = nil): DWORD; stdcall; overload; hWnd - Дескриптор окна. lpdwProcessId - Указатель на переменную типа DWord, после использования функции в него скопируется идентификатор потока создавшего окно.
- OpenProcess(dwDesiredAccess: DWORD; bInheritHandle: BOOL; dwProcessId: DWORD): THandle; stdcall; dwDesiredAccess - Устанавливает права доступа к объекту (мы будем получать полные права доступа PROCESS_ALL_ACCESS)/ bInheritHandle - Параметр дескриптора наследования. dwProcessId - идентификатор потока. С помощью этой функции мы получим права доступа к памяти объекта.
- ReadProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: DWORD): BOOL; stdcall; hProcess - Идентификатор объекта lpBaseAddress - Указатель на адрес из которого будем читать lpBuffer - Указатель на переменную-буфер, в которую будем читать значение из памяти. nSize - Количество байт, которое мы хотим прочитать. lpNumberOfBytesRead - Переменная-буфер, в которой устанавливается значение соответствующее количеству прочитанных байт. С помощью этой функции мы будем "подбираться" к нужному нам адресу.
- SendMessage(Wnd: HWnd; Msg, wParam: Word; lParam: Longint): Longint; Wnd - Окно, пpинимающее сообщение. Msg - Тип сообщения. (В нашем случае WM_KEYDOWM и WM_KEYUP - нажатие клавиши). wParam - Дополнительная инфоpмация о сообщении. (В нашем случае код виртуальной клавиши). lParam - Дополнительная инфоpмация о сообщении. (В нашем случае 0). С помощью этой функции мы будем посылать нажатие клавиши в окно клиента.
Прежде чем перейти к кодингу мы определим, как будет действовать наш Auto-Potter. В данном случае, дабы не нагромождать статью, мы воспользуемся самым простым путём:
Таймер будет обновлять показатели HP и MP на двух Gauge, и, если их процентное значение меньше требуемого, то происходит отправка нажатия клавиши в окно клиента.
Переходим к кодингу:
Создаем новый проект Delphi 7. На форму кидаем такие компоненты:
- Timer (Вкладка System)
- Gauge (Вкладка Samples) - 2 шт.
- Edit (Вкладка Standart) - 2 шт.
- Button (Вкладка Standart) - 2 шт.
Теперь поясню, для чего нужен каждый из них:
- Timer - основа нашей программы. Служит для обновления значений ХП и МП. (Свойство Enabled := False; Interval := 10);
- Кнопки Вкл и Выкл служат для включения и выключения Auto-Pottera.
- Два Gauge - мониторинг ХП и МП.
- Два Edit'a - значение ХП(1) и МП(2), при котором посылаем нажатие клавиши.
Перед кодингом мы должны знать оффсеты. На момент написания статьи это:
HP: [[[$B280C4]+$34]+$490]
MaxHP: [[[$B280C4]+$34]+$4D0]
MP: [[[$B280C4]+$34]+$494]
MaxMP: [[[$B280C4]+$34]+$4D4]
Теперь переключаемся в редактор кода и пишем всё по порядку:
- Объявим несколько глобальных переменных для удобства:
Код:KlientWindow:HWND; //Handle клиента ProcessId:Integer; hProcess:Integer; //Идентификатор объекта HPMinValue:Integer; //Минимальное значение ХП (из Edit1) MPMinValue:Integer; //Минимальное значение МП (из Edit2)
- Для начала создаем обработчик события кнопки Вкл - OnClick:
Код:procedure TForm1.Button1Click(Sender: TObject); begin KlientWindow := FindWindow(nil, PChar('Perfect World')); //Находим Handle окна GetWindowThreadProcessId(KlientWindow,@ProcessId); //Получаем Ид.П. hProcess := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId); //Открываем процесс с полным доступом HPMinValue := StrToInt(Edit1.Text); MPMinValue := StrToInt(Edit2.Text); Timer1.Enabled := True; end;
- Теперь переходим к нашему Timer - создаем обработчик события OnTimer:
Код:procedure TForm1.Timer1Timer(Sender: TObject); var HP,HPMax,MP,MPMax:Integer; WHP,WHPMax,WMP,WMPMax,NoB:DWord; begin try //На всякий случай заключаем в try...except, дабы в случай релога не засыпать пользователя ошибками ReadProcessMemory (hProcess, Pointer($B280C4), @WHP, sizeof(WHP), NoB); ReadProcessMemory (hProcess, Pointer(WHP+$34), @WHP, sizeof(WHP), NoB); ReadProcessMemory (hProcess, Pointer(WHP+$490), @WHP, sizeof(WHP), NoB); //Читаем значение HP HP := Integer(WHP); //Переводим Integer ReadProcessMemory (hProcess, Pointer($B280C4), @WHPMax, sizeof(WHPMax), NoB); ReadProcessMemory (hProcess, Pointer(WHPMax+$34), @WHPMax, sizeof(WHPMax), NoB); ReadProcessMemory (hProcess, Pointer(WHPMax+$4D0), @WHPMax, sizeof(WHPMax), NoB); //Читаем значение MaxHP HPMax := Integer(WHPMax); //Переводим Integer ReadProcessMemory (hProcess, Pointer($B280C4), @WMP, sizeof(WMP), NoB); ReadProcessMemory (hProcess, Pointer(WMP+$34), @WMP, sizeof(WMP), NoB); ReadProcessMemory (hProcess, Pointer(WMP+$494), @WMP, sizeof(WMP), NoB); //Читаем значение MP MP := Integer(WMP); //Переводим Integer ReadProcessMemory (hProcess, Pointer($B280C4), @WMPMax, sizeof(WMPMax), NoB); ReadProcessMemory (hProcess, Pointer(WMPMax+$34), @WMPMax, sizeof(WMPMax), NoB); ReadProcessMemory (hProcess, Pointer(WMPMax+$4D4), @WMPMax, sizeof(WMPMax), NoB); //Читаем значение MaxMP MPMax := Integer(WMPMax); //Переводим Integer Gauge1.Progress := round(HP * 100 / HPMax); //Присваиваем процентное значение HP Gauge1.Progress Gauge2.Progress := round(MP * 100 / MPMax); //Присваиваем процентное значение MP Gauge2.Progress except end; if Gauge1.Progress < HPMinValue then //Если HP меньше минимального begin SendMessage(KlientWindow, WM_KEYDOWN, VK_F1, 0); //то нажимаем SendMessage(KlientWindow, WM_KEYUP, VK_F1, 0); //клавишу F1 end; if Gauge2.Progress < MPMinValue then //Если MP меньше минимального begin SendMessage(KlientWindow, WM_KEYDOWN, VK_F2, 0); //то нажимаем SendMessage(KlientWindow, WM_KEYUP, VK_F2, 0); //клавишу F2 end; end;
- И завершаем это всё обработчиком события кнопки Выкл - OnClick:
Код:procedure TForm1.Button2Click(Sender: TObject); begin Timer1.Enabled := False; end;
Особая благодарность ''Хакерок
