📜 ⬆️ ⬇️

Creating a simple bot for WoW, programming routes

Introduction


I recently read a post in which the author spoke about his bot for trading at auction in the game World of Warcraft. His character ran a shuttle for short distances and performed specific actions strictly specified in the control program. Taking advantage of his ideas, I decided to go a little further: let the character be able to run along a route of at least five minutes, while it should be possible to record the route and the actions performed from the outside, without resorting to changing the management program itself. What I described is the development of the original post , however I tried to make it not mandatory to read it.
In the post I will describe
  1. The process of creating a trivial addon for the user interface of WoW in the Lua language
    • Serialization of data for transmission between parts of the system
    • Transfer using color pixels
  2. AutoIt character management process
    • Program to record actions
    • Program to play
    • Calculation of the direction of movement
    • Why you should not resort to reading / writing process memory
  3. Program management (ro) bot with commands that are understandable to non-programmer


A warning


I understand that the use of bots is prohibited by the license agreement Blizzard. This can lead to permanent account banning. I would be upset to learn that someone, taking advantage of the materials of this post, began to profit, worsening the economy of the game and causing disapproval of other players. And yes, I understand that what I am doing is not fair.

The purpose of the post is to tell about my experience and describe the difficulties I encountered, to find out from you what could have been done better.

Prehistory


Collecting

People far from the gaming industry may, without prejudice, skip this section. The WoW game has such an aspect - collecting. As in numismatics / philately, the more you have, for example, riding animals, the more pleasant it is for you. The process of getting is just as important. Some dragons require dozens of tasks ("achivok") to do, some to enter the top 2% of players in the world for character management skills (arena battles), something you can just buy for game currency, then - for real money in the store. Details can be read in another source , it's not about that. So, some of the collected items fall into the dungeon with a chance of about 0.5%. If the chance of loss is given to the player only once a day (sometimes a week), you will need time, commensurate with the year, for the races. And all for the sake of the moment of receiving the cherished suffering object. The more time and effort you have spent on the " farm " of an item, the more pleasant it is for you to get it in the end. In my experience, joy is very fleeting.
')
The dependence of the chance of loss on the number of attempts

A small digression. What is the likelihood that in six throws of the die would be “6”? It's obvious that . That is, our event is the opposite event to the failure of "6" for all six attempts.
Using the second remarkable limit , it is easy to show that .

And the probability of getting a horse with a “drop” chance of 1% for 100 attempts is approximately equal to 63%.

Some players, having made three hundred attempts, consider that they are just about lucky, because the probability of breaking off for such a large number of attempts is small. They are disappointed, because in the next 100 they will be lucky again only by 63%. And the old works give nothing.

Task

There is a dungeon. It is necessary to run through it with turns, perform a couple of simple tasks along the way. Kill the final boss and raise loot. In order not to run to the exit on foot, teleport to the nearest village and fly to the entrance. Repeat 5 times.

Dungeon map and route:


The maximum task. Launched and left for half an hour. The process is fully automatic.

The task is at least. Run and sit next to, read or play the guitar, glancing at the monitor. If necessary, occasionally help the character get out of an unexpected situation.

It is important to be able, if desired, to describe the route of another dungeon without getting into the code.

An approach


Motion and positioning

Just write down and release the keys, the position and mouse clicks will not work. It is more accurate to write something, only during playback, something completely different than what you expected will occur for the following reasons:
  1. You need to reproduce the original position of the character and the direction of his gaze. If you turn slightly in the opposite direction before the automatic race, you will run crookedly and sooner or later hit the wall
  2. If you have recorded the exact coordinates and angle, then you will not be able to place the character there either. He responds to the buttons like a bulldozer and turns the same way. And even an error of 1 degree in 100 meters will result in meters (doorway width)
  3. The time of pressing and releasing the buttons is also far from ideal. It is like moving around an apartment with eyes closed. It seems to be six steps forward and strictly to the right, but in fact, instead of the toilet, we find ourselves in the bathroom. Try also to open the door with eyes closed, not feeling the handle.

It is for these reasons that we need to be positioned in the world using coordinates. At the same time, it is necessary to check the position and make adjustments regularly, every 100 milliseconds.

 move 40.644 20.417 1.537
 move 40.526 20.411 1.537
 move 40.246 20.408 1.592
 move 39.974 20.414 1.592
 move 39.691 20.411 1.537
 move 39.417 20.388 1.510

This is an excerpt from the list of commands for moving the robot. The x , y coordinates and the angle of rotation in space are given in radians.

Keys and commands

On the way, you have to press the buttons and click with the mouse, open the doors, somewhere you have to wait. I would not like to write it all manually. And to search among the teams with coordinates (in the example above), where to enter something, is not very pleasant. Therefore, we will write one-time clicks on buttons and clicks in parallel with the recording of coordinates.

 pitch -0.89
 mouse right 942 498
 pause 10,000
 move 39.417 20.388 1.510
 key `
 pause 1000
 key {SPACE}
 move 39.142 20.364 1.510
 key {TAB}
 key 3
 key 3
 mouse right 963 622
 move 38.864 20.340 1.510
 move 38.653 20.321 1.510

Separately mention the "pitch" (tilt). If a character is looking at the horizon, it is zero. If the feet, the slope is negative. And in the sky - is positive. Measured in radians. He had to add as soon as the character wanted to fly.

Modules

Thus, we see the parts of the bot software and hardware complex :
  1. We will write the addon for the WoW interface , which will determine the character's position, azimuth, slope. The rules of the game add-ons are not prohibited, the specified information is available through the game API inside the add-on. He will draw it all on the screen. Sam addon is written in the language of Lua, because the game developers have decided.
  2. In the addon you can define something, you can draw, but you can't make the character move. Even in the addon you can not write to the file, ask the game where the walls are, where the enemies are, and what else is impossible. Therefore, we need to write an external control application . I did not become original and used AutoIt . This is really quick and convenient, albeit a bit wild for me, who are used to programming in strictly typed languages .
    • The application must be able to record my actions. Let's call this functionality Recorder
    • The application must be able to play the recording. Let's call the player
  3. We will store the recorded actions and coordinates in a text file line by line. They can and should be edited manually. Add crutches, remove debris. I will say right away, this is the most difficult.


Addon for WoW to Lua


Addon for WoW is a Lua script. The language is full, flexible, effective. Do nothing with the system. You can do everything with the game, but only within the framework of the API provided by the game. Since Blizzard does not want, or rather, prohibits players and entrepreneurs from writing bots, the API, in addition to the coordinates mentioned above, provides almost nothing useful and does not provide. Of course, useful for our needs. A good guide on writing addons immediately with a description of Lua can be found here . Well, I will describe my version.

Manifesto and GUI

Details and source . In the World of Warcraft \ Interface \ AddOns \ folder, create the HelloWorld folder. Put HelloWorld.toc into it.

 ## Interface: 50001
 ## Title: Hello, World!
 ## Notes: My first AddOn
 HelloWorld.lua

If I wanted to draw molds and buttons (and addons are usually needed for this), I would add HelloWorld.xml with the description of these buttons as the last line. AddOn Studio is a great help in the design and writing of such a GUI addon. Powerful tool based on MS Visual Studio.

But since I'm a minimalist, we won't draw this time. In addition, it will give me the opportunity to show that you can create forms dynamically from the script itself without using a designer. Consider the HelloWorld.lua . I edited it in Notepad ++. For the game to connect changes to HelloWorld.toc , you need to restart it entirely. But the changes in HelloWorld.lua can be picked up by writing the / reload command in the console. Therefore, the programming and debugging process is not so painful.

Slash commands

By the way, about debugging. I advise you to immediately add a slash command handler :

SLASH_HELLO_WORLD1 = '/helloworld'; function SlashCmdList.HELLO_WORLD(msg, editbox) local facing = GetPlayerFacing(); local pitch = GetUnitPitch("player"); local x, y = GetPlayerMapPosition("player"); print(format("HelloWorld %.2f %.2f %.2f %.2f", x*100, y*100, facing, pitch)); end 

To do this, we define a variable of the form SLASH_IMEN . Where NAME is unique for all add-ons, and n is either empty or is an ordinal number with 1. And we add a function with the name NAME into some object. It may seem strange to a C ++ programmer that we are not explicitly registering this handler function anywhere. Yes, and with a string variable bind purely by the variable name. But such is the power and magic of Lua.

Now the command in the WoW console / helloworld will perform the actions you require : it will allow you to display debugging information, and for the simplest add-ons, it will actually produce all that you wrote them for.

Well, and then I showed the WoW API, which provides the required information.

Event handler

The system for working with GUI in addon is similar to the work of Windows with its messages and their processing.

 local EventFrame = CreateFrame("Frame") function EventFrame:OnEvent(event, ...) print("HelloWorld:", event) self[event](self, ...) end EventFrame:SetScript("OnEvent", EventFrame.OnEvent) EventFrame:RegisterEvent("PLAYER_LOGIN") function EventFrame:PLAYER_LOGIN() --   end function EventFrame:OnUpdate() --    end EventFrame:SetScript("OnUpdate", EventFrame.OnUpdate) 

We create a frame of type " Frame " and connect to the actions of " OnEvent " and " OnUpdate " commands

 EventFrame:SetScript("OnEvent", EventFrame.OnEvent) EventFrame:SetScript("OnUpdate", EventFrame.OnUpdate) 

The OnUpdate handler will be called after each frame - what we need to update the coordinates. And OnEvent will be called on other desired events. From it we will pull the corresponding functions:

 self[event](self, ...) 

What is even more convenient in Lua, is that such a construction can invoke unique view handlers.

 function EventFrame:PLAYER_LOGIN() 

Here, PLAYER_LOGIN is an event that is sent to all the frames upon entering the world and restarting the user interface. Other events: PLAYER_LEAVE_COMBAT , QUEST_FINISHED , PLAYER_EQUIPMENT_CHANGED , PLAYER_DEAD , - and a lot more. The full list can be found here . Register the event that we want to handle with the command

 EventFrame:RegisterEvent("PLAYER_LOGIN") 


Information transfer

Since in the addon nothing can be done with the system, we will transmit information from the WoW of another part of the bot by changing the color of the pixels. As rednaxi did in his post . But only we will transmit not one bit of information in color, but we will serialize the data and transmit them more.

Drawing

Since we also need frames for drawing, we will create them

 local HelloWorld1 = CreateFrame("Frame", nil, UIParent) local HelloWorld2 = CreateFrame("Frame", nil, UIParent) function EventFrame:PLAYER_LOGIN() HelloWorld1:SetFrameStrata("BACKGROUND") HelloWorld1:SetWidth(10) HelloWorld1:SetHeight(10) HelloWorld1.texture = HelloWorld1:CreateTexture(nil,"BACKGROUND") HelloWorld1.texture:SetAllPoints(HelloWorld1) HelloWorld1:SetPoint("TOPLEFT",0,0) HelloWorld1:Show() HelloWorld2:SetFrameStrata("BACKGROUND") HelloWorld2:SetWidth(10) HelloWorld2:SetHeight(10) HelloWorld2.texture = HelloWorld2:CreateTexture(nil,"BACKGROUND") HelloWorld2.texture:SetAllPoints(HelloWorld2) HelloWorld2:SetPoint("TOPLEFT",10,0) HelloWorld2:Show() end 

I think everything is clear and without comment. For details on each method, look for yourself.

Serialization

We have two coordinates and two angles. All are floating point numbers. The color components of the pixels in the addon are also floating point numbers, but from 0.0 to 1.0 . In addition, it is known that the color component will remain in one byte. At first, I saved each coordinate in one color component. As a result, the positioning accuracy in the location was 1/255 of the map size. It turned out like in GPS : it seems there are coordinates, but driving on an automatic machine (controlling a computer without a person) on the way through the navigator will not work. So I had to give two bytes. How is it more advantageous to save one fractional number of two bytes? I did so

 local x1, x2 = math.modf(x*255) 

The modf function returns the integer and fractional parts of a number, separated by commas. It uses parallel assignment - another useful Lua fiction.

With this approach, I use the full power of each of the two bytes in which the coordinate is stored. Well, the azimuth and tilt are not so exacting for accuracy, so long as they fit into the 0.0-1.0 segment. It turns out like this:

 local math = getfenv(0).math function EventFrame:OnUpdate() local facing = GetPlayerFacing(); local pitch = GetUnitPitch("player"); local x, y = GetPlayerMapPosition("player"); local x1, x2 = math.modf(x*255) local y1, y2 = math.modf(y*255) HelloWorld1.texture:SetTexture(x1/255, x2, facing/7) HelloWorld2.texture:SetTexture(y1/255, y2, pitch/4+0.5) end 

Now, when moving around the world in the upper left corner, two 10x10 squares will randomly change their color.
Full text HelloWorld.lua
 local math = getfenv(0).math SLASH_HELLO_WORLD1 = '/helloworld'; local EventFrame = CreateFrame("Frame") local HelloWorld1 = CreateFrame("Frame", nil, UIParent) local HelloWorld2 = CreateFrame("Frame", nil, UIParent) function EventFrame:OnEvent(event, ...) print("HelloWorld:", event) self[event](self, ...) end EventFrame:SetScript("OnEvent", EventFrame.OnEvent) EventFrame:RegisterEvent("PLAYER_LOGIN") function EventFrame:PLAYER_LOGIN() HelloWorld1:SetFrameStrata("BACKGROUND") HelloWorld1:SetWidth(10) HelloWorld1:SetHeight(10) HelloWorld1.texture = HelloWorld1:CreateTexture(nil,"BACKGROUND") HelloWorld1.texture:SetAllPoints(HelloWorld1) HelloWorld1:SetPoint("TOPLEFT",0,0) HelloWorld1:Show() HelloWorld2:SetFrameStrata("BACKGROUND") HelloWorld2:SetWidth(10) HelloWorld2:SetHeight(10) HelloWorld2.texture = HelloWorld2:CreateTexture(nil,"BACKGROUND") HelloWorld2.texture:SetAllPoints(HelloWorld2) HelloWorld2:SetPoint("TOPLEFT",10,0) HelloWorld2:Show() end function EventFrame:OnUpdate() local facing = GetPlayerFacing(); local pitch = GetUnitPitch("player"); local x, y = GetPlayerMapPosition("player"); local x1, x2 = math.modf(x*255) local y1, y2 = math.modf(y*255) HelloWorld1.texture:SetTexture(x1/255, x2, facing/7) HelloWorld2.texture:SetTexture(y1/255, y2, pitch/4+0.5) end EventFrame:SetScript("OnUpdate", EventFrame.OnUpdate) function SlashCmdList.HELLO_WORLD(msg, editbox) local facing = GetPlayerFacing(); local pitch = GetUnitPitch("player"); local x, y = GetPlayerMapPosition("player"); print(format("HelloWorld %.2f %.2f %.2f %.2f", x*100, y*100, facing, pitch)); end 



Conclusion of the first part


Repeat what was said
  1. We considered the probability of obtaining the item for a large number of attempts.
  2. Designated the task, developed an approach to the solution, divided into modules
  3. We reviewed the simplest WoW add-on code in Lua
    • learned to transfer coordinates using color
    • learned to draw, handle events
    • learned to handle slash commands

    Now you are ready to write your addon.

And if you are developing something and want to allow yourself or users to participate, flexibly adjust the software for themselves using your API (for example, write artificial intelligence of opponents, allow brokers to place positions based on quotes, allow administrators to perform their actions on the results user systems inventory, etc.), then know that Lua is very flexible and very easy to build. Consider this opportunity.

Well, in the next part we will talk about
  1. writing the keys and coordinates in the AutoIt language
  2. Writing Player'a instructions for the bot
  3. math 2D how to navigate in the Cartesian coordinate system without the cosine theorem
  4. control of the robot with an insufficient number of sensors
  5. bots countermeasures

Change from August 14
Added a link to the second part.

Source: https://habr.com/ru/post/189792/


All Articles