📜 ⬆️ ⬇️

Creating a simple bot for the online game world of warcraft

I think the theme of bots does not leave indifferent any player in online games. Someone they annoy, someone is interested in them, and someone uses them. There are a number of people, rather small relative to the other three groups - these are the people who develop these bots.
I propose to join this small caste of people and see the bot development process from the inside.

Prehistory


One weekend, I went to my character in the world of warcraft. There was nothing to do in the game - all the raid bosses have already been killed, there are no friends to go to the arena, only quests and unhurried gold mining remain. I don’t really like quests and usually spend my free time in the game around the auction - I’m buying it with the help of special add-ons which are cheaper and then sell more expensive, winning by the difference in price.

Accordingly, the thought came to mind to automate these routine actions - to come to the auction, launch the addon, click on the buttons in it, reach the mailbox, pick up things from the post office, return to the auction and start the addon again, and then in a circle.
')
Auto It was chosen as a tool for implementation, but it turned out that the task was not as trivial as it looked at first glance, and I had to plug in additional tools. I’ll say right away that I didn’t use any “dirty” methods like reading the process memory or intercepting traffic, only autoit to simulate mouse clicks and keyboards and read pixel colors from the screen.



A warning


You perform all the described actions at your own peril and risk, the rules of the game are prohibited and may lead to permanent ban of the account.

Movement


Actually, probably the most interesting topic when developing a bot is the implementation of its movement around the world. At first, I thought that there would be no problems with this - the way is simple: the auction is the building where NPCs are located on such a platform, with whom you need to talk to start trading, and the mailbox is right at the exit of the auction, a little to the left. The character therefore goes all the time like the letter "G", back and forth



I thought that just a notch how much time you need to press "forward", how much "right" or "left" to come to the right point and just make it hard. But the reality has shown that it is impossible to do this: if you do this, then there is a problem with ping: the character sometimes ran too much, sometimes turned too much, the method was completely inoperative.

But I did not despair and began to think what to do next. It would seem that the next decision is to somehow determine where the character is at the moment, and depending on this, calculate where to go next. Only how to get information about the position and angle of rotation of the character without reading the memory of the game? Analyzing the whole picture or some of its individual parts is quite difficult, a screenshot of the exit from the auction:


After some deliberation, a solution to the problem was invented: it is necessary to write an add-on for WoW, which will receive data on the current position of the character (coordinates and rotation of the character) and give commands where to move the character. Outwardly, it should look like a colored “semaphore” that will display the necessary action: move forward, right or left, or turn. When a character arrives at the desired point, the semaphore should symbolize that the character has come into place and the next action must be performed. Since my character moves between two points, I decided to make 2 semaphores: one will command on the way to the auctioneer, and the second - on the way to the post. I sort of figured out the algorithm, then there will be code examples (xml - addon templates, lua are addon sources, and autoit), sometimes crooked and dirty, but I think forgivable: I wrote these languages ​​for the first time, and didn’t figure it out yet.

To implement semaphores, I had to study the process of developing add-ons for WoW, it is very well described in WoWwiki

The very first version of the semaphore, it showed only if I turned to face the auctioneer at the moment, and highlighted in green the corresponding blocks:


As a basis for my addon, I took hello world! addon described in vvviki.
Initially, he did not have any graphic elements, respectively, it was necessary to add the output of rectangles to it, and also to hang up the handler. As a result, my HelloWorld.xml began to look something like this:
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd"> <Script File="HelloWorld.lua"/> <!--  -    --> <Frame name="myTabContainerFrame" toplevel="true" frameStrata="DIALOG" movable="true" enableMouse="true" hidden="false" parent="UIParent"> <!--   --> <Size> <AbsDimension x="20" y="120"/> </Size> <!--  -     ,     20  --> <Anchors> <Anchor point="TOPLEFT"> <Offset><AbsDimension x="20" y="-20"/></Offset> </Anchor> </Anchors> <!--   --> <Frames> <!--  -      --> <Frame name="myGoToAucFrame"> <!--  -   ,      --> <Size> <AbsDimension x="20" y="60"/> </Size> <!--  -       --> <Anchors> <Anchor point="TOPLEFT" /> </Anchors> <Frames> <!--    -       --> <Frame name="myTabPage1" hidden="false"> <!--      --> <Anchors> <Anchor point="TOPLEFT" /> </Anchors> <!--  -    ,   --> <Size> <AbsDimension x="20" y="10"/> </Size> <!--   --> <Layers> <Layer level="ARTWORK"> <!--  -       autoit --> <FontString inherits="GameFontNormal" text="*"> <Anchors> <Anchor point="TOPLEFT" relativeTo="$parent" /> </Anchors> </FontString> <!--  -      --> <Texture name="PlayerAucViewTrue"> <Size> <AbsDimension x="20" y="20" /> </Size> <Anchors> <Achor point="BOTTOM" relativePoint="BOTTOM" relativeTo="UIParent" /> </Anchors> </Texture> </Layer> </Layers> <Frames> </Frames> </Frame> <!--  ,       --> <Frame name="myGoToAucFrame2" hidden="false"> <Anchors> <!--   10  ,      --> <Anchor point="TOPLEFT"> <Offset><AbsDimension x="0" y="-10"/></Offset> </Anchor> </Anchors> <!--  -      -   ,     --> <Size> <AbsDimension x="10" y="10"/> </Size> <Layers> <Layer level="ARTWORK"> <FontString inherits="GameFontNormal" text="<"> <Anchors> <Anchor point="TOPLEFT" relativeTo="$parent"> <Offset> <AbsDimension x="0" y="0"/> </Offset> </Anchor> </Anchors> </FontString> <Texture name="PlayerAucViewLeft"> <Size> <AbsDimension x="20" y="20" /> </Size> <Anchors> <Achor point="BOTTOM" relativePoint="BOTTOM" relativeTo="UIParent" /> </Anchors> </Texture> </Layer> </Layers> <Frames> </Frames> </Frame> <!--        --> </Frames> </Frame> </Frames> <!--   -          --> <Scripts> <OnLoad> Semafor_Onload(); </OnLoad> <OnUpdate> CheckPosition(); </OnUpdate> </Scripts> </Frame> </Ui> 


Accordingly, in HelloWorld.lua, you must write the appropriate handlers. To find out at what angle the character is currently rotated, use the GetPlayerFacing function, which returns an angle relative to north in radians. Experimentally, it was found that when a character looks directly at the auctioneer - this is an angle of 5.42 radians. But it’s quite difficult to turn a character precisely at this angle, so we assume a small scatter (5.35 - 5.5 radians).
 function Semafor_Onload() print("Hi!"); end function CheckPosition() local facing = GetPlayerFacing(); --                 if(facing <= 5.50 and facing >= 5.35) then PlayerAucViewTrue:SetTexture(0,1,0); --   ,    RGB. 1,0,0 - , 0,1,0 -  PlayerAucViewLeft:SetTexture(1,0,0); PlayerAucViewRight:SetTexture(1,0,0); elseif ((facing > 5.50))then PlayerAucViewTrue:SetTexture(1,0,0); PlayerAucViewLeft:SetTexture(1,0,0); PlayerAucViewRight:SetTexture(0,1,0); elseif (facing < 5.35) then PlayerAucViewTrue:SetTexture(1,0,0); PlayerAucViewLeft:SetTexture(0,1,0); PlayerAucViewRight:SetTexture(1,0,0); end end 


Now we will write a script on autoit, which will look at what color is currently in the semaphore, and rotate the character in the required direction, and at the same time create the framework of our application for automated auction trading. It was empirically discovered that the color of the 65280 pixel is green.
 Global $WinName = "World of Warcraft" Global $state = "stop" Opt("PixelCoordMode", 2) ;          Opt("MouseCoordMode", 2) ;          HotKeySet("{NUMPAD1}", "GoRotate") HotKeySet("{NUMPAD3}", "_Exit") WinActivate($WinName) WinWaitActive($WinName) While 1 sleep(10) Running();    ,        WEnd Func _Exit() Exit EndFunc Func GoRotate() $state = "rotating" EndFunc Func Running() Switch $state Case "rotating" Rotating() EndSwitch EndFunc Func Rotating() While $state = "rotating" ;  ,   ,  ,    ,    paint     $angleOkColor = PixelGetColor(32,24) $angleLeftColor = PixelGetColor(29,36) $angleRightColor = PixelGetColor(40,32) if $angleOkColor = 65280 Then $state = "starttrading" ;       -   ElseIf $pxAngleLeftColor = 65280 Then;   Send("{LEFT down}");   ""       ,   - ,    -  While PixelGetColor(29,36) = 65280 sleep(2) Wend Send("{LEFT up}") ElseIf $pxAngleRightColor = 65280 Then ;   -  ,     Send("{RIGHT down}"); sleep(20); Send("{RIGHT up}"); EndIf WEnd EndFunc 


Everything, save the addon, launch WoW, launch the AutoIt script and look at the character, who himself turns his face in the right direction. But besides the turns, it is also necessary to realize moving the character back and forth and left-right so that he can come to the auctioneer from the mailbox. It is fortunate that there are no obstacles in the way of the character, otherwise the task would be more difficult.

And just add additional frames to our xml for 5 more semaphore buttons: Ok! Position, move forward, move back, move right and move left, and in the lua file we add a code that will show where we need to move at the moment. The reference coordinates where we need to be located we know. It would seem too simple, but it was not there - as we saw on the map, and we already figured out when we were making turns, we move at an angle to the north. Those. when moving, we constantly change both coordinates of the character. It is not very convenient to consider where to go further, therefore we will use the formulas known from the school year to rotate the coordinate system at a given angle (in our case - 5.42 radians)



Now, when moving to / from the auctioneer and left-right, we will have only one coordinate to change. Let's add the corresponding code to our lua file and it will look something like the following:
 function Semafor_Onload() print("Hi! All done!"); end function CheckPosition() local facing = GetPlayerFacing(); SetMapToCurrentZone(); local posX, posY = GetPlayerMapPosition("player"); --       (    5.42 ) newPosX = posX*math.cos(5.42) + posY*math.sin(5.42); newPosY = -posX*math.sin(5.42) + posY*math.cos(5.42); newPosX = -newPosX; --     :) --                 if(facing <= 5.50 and facing >= 5.35) then PlayerAucViewTrue:SetTexture(0,1,0); PlayerAucViewLeft:SetTexture(1,0,0); PlayerAucViewRight:SetTexture(1,0,0); elseif ((facing > 5.50 and facing < 6.5))then PlayerAucViewTrue:SetTexture(1,0,0); PlayerAucViewLeft:SetTexture(1,0,0); PlayerAucViewRight:SetTexture(0,1,0); elseif (facing < 5.35) then PlayerAucViewTrue:SetTexture(1,0,0); PlayerAucViewLeft:SetTexture(0,1,0); PlayerAucViewRight:SetTexture(1,0,0); end --            if (newPosX <= 0.207 and newPosY <=0.889 and newPosY >=0.8875) then PlayerGoForvard:SetTexture(1,0,0); PlayerGoBack:SetTexture(1,0,0); PlayerGoLeft:SetTexture(1,0,0); PlayerGoRight:SetTexture(1,0,0); PlayerOnAuc:SetTexture(0,1,0); elseif (newPosY > 0.889) then PlayerGoForvard:SetTexture(1,0,0); PlayerGoBack:SetTexture(1,0,0); PlayerGoLeft:SetTexture(0,1,0); PlayerGoRight:SetTexture(1,0,0); PlayerOnAuc:SetTexture(1,0,0); elseif (newPosY < 0.8875) then PlayerGoForvard:SetTexture(1,0,0); PlayerGoBack:SetTexture(1,0,0); PlayerGoLeft:SetTexture(1,0,0); PlayerGoRight:SetTexture(0,1,0); PlayerOnAuc:SetTexture(1,0,0); elseif (newPosX > 0.207) then PlayerGoForvard:SetTexture(0,1,0); PlayerGoBack:SetTexture(1,0,0); PlayerGoLeft:SetTexture(1,0,0); PlayerGoRight:SetTexture(1,0,0); PlayerOnAuc:SetTexture(1,0,0); end end 


That's it, now we’ve got something like this semaphore:

It remains to add by analogy the AutoIt script so that when the semaphore signals light up, it sends the corresponding buttons to the game. The only thing I encountered was that it doesn’t allow the letter buttons (A / D) to be sent normally, so I had to bind the strafe on F6 / F7.

That's all, after that we get a character who automatically turns, and then runs and stands near the auctioneer.

The volume of the text was already quite large, if the topic turns out to be interesting for the Habrasoobshchestvo, then in the following sections I will tell about how I implemented the interaction with the auctioneer and how I received mail from the mailbox.

Today at 12 o'clock I put the bot to spin at the auction, during its work (about 9 hours) it took 3 times to interfere with its work - once it was stuck in the sticking out wall elements, then I slightly rewrote the running algorithm in the direction of the mail, and 2 times I missed the mailbox - I need to fix it before my hands reach it.
Profit per day:

Not bad, provided that I did not participate in the process completely :)

Upd:
the second part of

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


All Articles