PART 3 - SPECIALLY CHALLENGING STUFF
---------------------------------------------------------------------
Once you can properly read your position coordinates in the game, and send keystrokes and mouse clicks to the game, next thing you must do is a navigation system. This is somehow challenging, but doable. It consists in:
1. Read your coordinates from the game (from the addon/weak aura)
2. Read your actual facing direction (are you looking north, south, east?)
3. Turn your char to the direction you must move.
4. Check the distance between your current position and desired position.
5. Press W until this distance reaches "zero"
Unfortunetly, all you got is your currently and desired X and Y coordinates, and there is no other way to convert that in real clicks and keypress, but using a lot of trigonometry and math. If you were not a good math student, and hated trigonometry. this may be not for you.
UNDERSTANDING RADIANS
---------------------------------------------
First, you must understand how game deals with angles. You probably know that a full turn is 360 degrees. But wow does not use degrees. Instead, angles are presented in RADIANS. One radian = aprox. 57 degree. To be more precise, 360 degrees = 2 pi radians. Got it? "The circumference subtends an angle of 2π radians.". Keep that in mind.
TURNING SPEED
----------------------------
When you press game keys A or D, you will take exactly 2 seconds to make a full turn. That translates into one pi radian per second, or 180 degrees per second. So, if you want to make a 90 degree turn to the right, you must press the D key for 500 miliseconds, or the A key for (2-500) miliseconds.
DISCOVERING WHERE TO PLAYER IS FACING
-------------------------------------------------------------------------
This can be done easily with a Lua function (included in the Weak Auras at the first post), but the function is:
Code:
fa = GetPlayerFacing()/6.2832
The code above will return your currently facing postition. I have divided it by 2 pi, so that you get a percentage result. Remember that when you transform it in a colour element, result will be from 0-255 range.
DISCOVER THE ANGLE NECESSARY TO TURN FROM CURRENT(X,Y) to TARGET(X,Y)
----------------------------------------------------------------------------------------------------------------------------------------
This is tricky. The only way to do this is using the trigonometric function atan2. This is not much friendly. The most important thing you need to know is that this function uses Y before X at the arguments. I am very sorry for you, but there is no easy way to do that without using trigonometry functions. It will be something like this:
Code:
double getangle(double y1, double y2, double x1, double x2)
{
double ang = Math.Atan2(x1 - x2, y1 - y2)/Math.PI;
if (ang < 0) ang += 2; // this is used to avoind negative numbers.
return Math.Round(ang*1000);
}
METHOD TO CHECK DISTANCE BETWEEN TWO COORDINATES (TARGET -> CURRENT)
-------------------------------------------------------------------------------------------------------------------------------------------
For the below code, I used a simple struct to store locations, to make things easier to manage:
Code:
// Estrutura de coordenada
struct loc
{
public int x;
public int y;
}
struct loc
{
public int x;
public int y;
}
You will need this to know if you arrived at your destination.
Code:
int dist(loc orig, loc tar)
{
double distance = (Math.Sqrt(Math.Pow(Math.Abs(orig.x - tar.x), 2) +
Math.Pow(Math.Abs(orig.y - tar.y), 2))); // formula da distancia entre pontos
int temp = (int)distance; // converte double para int
// tbdist.Text = temp.ToString(); // escreve distancia no campo do texto, for debug
return temp; // retorna distancia entre os pontos
}
GET YAW (TURNING ANGLE) NEEDED
-------------------------------------------------------------
Once you got actual player facing position, and you know the needed new face, you need to discover the yaw (angle diference), that means, the necessary turn. You must turn to the lowest side (e.g turn 10 degrees left, not 350 degrees right).
When measuring angles, and angle needed to turn, you can use degrees (0-360), radians (0-2 pi), or a 0-2000 scale.
Keep in mind that when you press A or D, your char will take 2 seconds to make a full turn, so we know that:
- It turns 90 degrees per second, or 1 radian per second, or 1000 miliseconds per second.
C# uses natively radians, but I recommend for you to use a 0-2000 scale, so that you just use the number of miliseconds needed to turn. For example, if you have a method called "turn" that have as arguments the angle.... you would have, to turn a full 360 degree turn:
turn(2*math.pi) = rads
turn(360) = degs
turn(2000) = using a milisecond scale. <--- EASIER METHOD.
I used the functions below as example, but they have my own variables so will not be usable instantly:
First you read the angle from the addon:
Code:
Color cor = GetColorAt(x,y); // X and Y are screen location of addon at first post. Remember that
Double facing = Math.Round(cor.B * 7.81); // Convert from 0-255 to 0-2000 range.
Then you calculate angle needed to your new coordinates
Code:
//Calcula o ângulo até a coordenada alvo; norte = 0; sul = pi
double getangle(double y1, double y2, double x1, double x2)
{
double ang = Math.Atan2(x1 - x2, y1 - y2)/Math.PI;
if (ang < 0) ang += 2;
return Math.Round(ang*1000);
}
Then you calculated the yaw (turning) needed
Code:
void getyaw(loc orig, loc tar)
{
// Retirar as linhas abaixo
getpos();
orig = player.pos; // this is the xy struct, shown above
tar = t;
double yaw_result;
pitch = getangle(orig.y, tar.y, orig.x, tar.x); // remember the pixel struct above
// Calcula angulo necessacio (yaw)
if (pitch > facing) yaw_result = pitch - facing;
else if (pitch < facing) yaw_result = 2000 -facing + pitch; // I used a 0-2000 range to make stuff easier.
else yaw_result = 0;
yaw = yaw_result;
}
I know that is not easy, but theres no easier way to make a navigation method.
Once you know this all, you just make a while loop to move, keep checking angles and turning while you walk, until distance < 10 or so. You must do this way to avoid robot-like movement (stop-turn-walk....) . Code below is just exemple, as it has my own variables and wont be usable "as is", but can be used to learn algorithim structure for walking from point a to point b, so I will comment it heavliy
Code:
do // start moving loop
{
getstats(ref player); // Check player stats (I need to check if entered combat while moving)
if (player.combat==true) // oh no... entered combat while moving.
{
combatloop(); // start combat routine until mob dead.
aperta(WKEY, 0); // start walking again after combat.
}
temp++;
focawow(); // focus wow process
getpos(); // check my position
getyaw(player.pos, t); // check yaw (turning) neede from my position to my target position.
facepoint(); // turn to my target.
wait(500); // walk a bit....
if (temp % 7 == 0) aperta(SPACEBAR, 100); // jump sometimes... every 3 seconds... just as stuck prevention.
if (temp % 2 == 0) // every 2 seconds will check if distance not changed == I am stuck!
{
if (player.pos.x == oldloc.x && player.pos.y == oldloc.y) // I did not move???
// no...
{
unstuck(); // start unstuck routine - code is not here
}
oldloc = player.pos; // stores new position to keep checking if it changed (stuck)
}
}
UNSTUCK ROUTINE
--------------------------------
It is necessary. From my experience, jumping every 3 seconds and the unstuck routine below will make you move without problems thru the game. (walks around trees and things like that)
You must include this routine at your walk loop.
Code:
void unstuck()
{
focawow(); // focus wow window, method stated above
if (!enroscou) aperta(SPACEBAR,100); // first time it is stuck, try to jump.... it usually solves the thing.
if (enroscou) // (player.spd < 100) // second stuck, will try to walk around the object, as jumping not solved
{
aperta(WKEY); // Start walking
aperta(EKEY, 700); // turn southeast (should be D key for most people; I use E at my key binds)
aperta(WKEY, 1000); // walk for one second
}
aperta(WKEY, 0); // start walking again , 0 time means it will walk until stopped
enroscou = !enroscou; // just to alternate jump-circle around
}
Well guys, that was what I had. If anyone has an specific question or need help at implementing stuff about, feel free to ask.
Currently I am working on multi-threading to make interface responsive. Unfortunatly unsuscessful so far. I am also having issues to make bot face my target too.
Thank you!!
----------------------------------------------------------------------------------------------------------------
/// EXTRA INFO- NEW USEFUL / ADVANCED METHODS - ADDED FEBRUARY 2020
------------------------------------------------------------------------------------------------------------------
After having the grind bot fully working (best class so far to me was a paladin), I have some feedback, and extra methods / algorithims to share.
Best class overall for make a bot for me was a paladin. They have high survivability, can heal, and look less botish while playing (as a paladin itself does not move much). Combat routine is easier too, as all you got to do is keep Seal of Righteousness up and judge on cooldown.
I came with some issues, that i would like to share.
First: sometimes when waiting for things like eating, drinking, looting or skinning, mobs attacked us, and if we use a simple "wait" command do do these things, if you get attacked while eating you would wait a lot without reacting. So you better do an "active" wait method, that will check for combat status every 1 second, and you use this to wait while eating, looting, or skinning, so that if you enter combat, bot will stop waiting and react properly. The "wait" function, when used to drink or eat, should also check for "full life" status, so that you stop drinking if your mana gets to 100%. that will save you a lot of time. Check the pseudo-code below:
----------------------------------------------
ACTIVE WAITING METHODS
-----------------------------------------------
Active waiting while looting is needed, as the ammount of time needed to loot may vary depending the quantity of itens to loot. So no need wo wait beyeond the necessary. As wow will lose target from mob after you have looted, just check for losing target while you wait for loot complete.
Code:
//----------------------------------
// WAIT WHILE LOOTING
//---------------------------------
bool waitloot(int seconds)
{
check-my-status();
bool loot_sucessful = true;
for (int i = 0; i < seconds; i++)
{
wait(1000); // wait 1 second
check-my-status();
if (me.combat) { wait_sucessful = false; break; }
if (me.tarhp==0) { return loot_sucessful ; } // wow loses target after loot ok
}
return loot_sucessful ;
Waiting to eat / drink may vary in time , as you may recover life faster, or even someone may heal you. So you should check your hp every 1 second while eating / drinking, or even casting long heals like 2,5s heals. Also should check for combat state while waiting. Do it eveyr 1 second.
Code:
//-------------------------------------------------------------------------------
// WAIT WHILE EATING / DRINKING / LONG HEALS
//-------------------------------------------------------------------------------
bool espera(int segundos,int hp=0) // Arguments: seconds, and hp/mana treshold. If 101 means mana.
{
checkme(); // check status
bool deucerto = true; // waited fine return
for (int i=0;i<segundos;i++) // wait 1 second, loop
{
checkme();
if (me.combat) { deucerto = false; break; } // entered combat, stop waiting
if (hp == 101 && me.mana > 90) break; // stop drinking if mana 90%
if (hp!= 101 && hp>0 && me.hp >= hp) { break; } // stop eating if life ok
wait(1000); // wait 1 second
}
return deucerto; // returns successful.
}