SRL Archive - OSR Advanced Scripting Tutorials

TPA's explained.


mastaraymond

2008-03-16 10:26:28

Hello,

Lots of you guys see TPA's as a big word, a angry word, because you think it's hard! Let me tell you something: "TPA's are not hard!". If you want to read this, I would advise to read some tutorials about array's and records first.

What is a TPA?

An TPA is a shortcut for TPointArray. Before we go to the Array part, I think its useful to start with the basic of the TPA, namely the TPoint.
The TPoint is a record that holds the x and y coord of a point. When you got a window, the coord of the upper left corner is 0,0. When going to the right you will increase the X, and going down you will increase the Y (While in math it works different). One point is handy, for example when you want to find a color that's the most close to the middle of your screen, for example a rock. But now we want to find all the rocks on the screen.. Howdy how! How? We will need to find all the coordinates of colors of the rock, and where do we store them? Exactly in a TPointArray (An array of TPoints). I always see TPointArray's as an collection of points that have a relation with each-other.

Basically this is how its declared. (This is inside scar, this is just to show you how its built..)

program Hoi;
type
TPoint = record
x,y : Integer;
end;
TPointArray = Array of TPoint;
begin
end.
Umkay, but what now? How do I use the TPA?

Lets make a TPA first, just to feel how it works!
program Hoi;
var
TPA : TPointArray; // We declare the TPA! Party party.
I,L : Integer; //I will be our for loop variable, L will be the length of the TPA.
begin
SetLength(TPA, 5); // Give the TPA an length of 5, 0..4.
L := High(TPA); // This returns the highest element of the array, in this case 4.
For I := 0 to L do //Loop through the TPA!
begin;
TPA[I].x := I * 2; //Give the x coord a weird value.
TPA[I].y := I * 4; //Give the y coord a weird value.
end;
//Now we have the TPA loaded into memory! Lets writeln it!
ClearDebug;
For I := 0 to L do
begin;
Writeln(''); //Empty line
Writeln('TPA['+inttostr(I)+'].x = ' + Inttostr(TPA[I].x));//Writeln the X value.
Writeln('TPA['+inttostr(I)+'].y = ' + Inttostr(TPA[I].y));//Writeln the Y value.
end;
Writeln('');
end.
Ok, now you know howto set the TPA yourself, now lets do the part where we want to get the coords of all the spot where the color is found!
Scar help us out! Here we go:

FindColorsTolerance(var Points : TPointArray; Color, xs, ys, xe , ye , Tolerance : Integer): Boolean;
FindColorsSpiralTolerance(x,y : Integer; var Points : TPointArray; color, xs,ys,ye : integer; Tolerance : Integer);
This two functions are used to find an color and save all the found coordinates in the TPointArray.
The main difference between these function is that FindColorsSpiralTolerance spirals from the given x and y coordinate (This can be very handy when you want to find the closet rock).
The FindColorsTolerance just starts in the xs, ys and ends in the xe, ye.
I hope you all know howto fill in the parameters, if not close this page.
Now you have all the coords of the color found on the screen.. But what now?
Exactly, thats why I want to introduce something new, the TPointArrayArray or T2DPointArray. (Both the same).
This is just an Array of TPointArray, an multi-dimensional array. I hope you know what that means.. TPA[0][0].x := 5; TPA[0][0].y := 1337. Like that.
Why would we want 2D array's? Say you want to find all the rocks on the screen. First you find all the colors. But now you have a big TPA with the points of all the rocks, so you must split the TPA up into different TPA's all including one rock.. You might be thinking "How?". Thats why we got the WizzyPlugin, but that later.

First start with an example FindBobsServant taken from SRL. (The famous error giving one..)

function FindBobsServant: Boolean;
var
ATPA: T2DPointArray; //Declare the Array of TPointArray;
TPA: TPointArray; //Declare the TPointArray;
I, cts ,x ,y, k: Integer; //x,y coords. K highest element. I for loop. CTS is for ColorToleranceSpeed.
begin
if (not LoggedIn) then Exit; //Failsafe.
cts := GetColorToleranceSpeed;//Get the current CTS.
ColorToleranceSpeed(2); //Set the CTS to 2. (More about this in another tutorials..
SetColorspeed2Modifiers(0.10, 25);
FindColorsTolerance(TPA, 5143728, MSX1, MSY1, MSX2, MSY2, 20);
//Find the color 5143728 with a certain tolerance. We search in an area from the left,upper corner to the
//mainscreen x2 and y2.
//All the points that contain this color with a certain tolerance will be stored into the TPA.

//Its possible that the trees are also stored into this TPA so we need something to sort the trees
//out of the TPA...
//There are multiple splitting methods, mainly TPAToATPA and SplitTPA.
//TPAToATPA works as the following, it finds all the points in an TPA which are close to eachother
//(Close in the range of W and H). All these points are added to an seperate TPA..
//In the end it will add all these TPA's to an T2DPointArray.
//So it contains several TPointArray's all points close to eachother.
ATPA := TPAtoATPAEx(TPA, 50, 50);

//Now we want to sort the T2DPointArray to the middle of the screen.
//Meaning that it sorts all of the sub TPointArray's to the closet distance.
//So say we have trees in the T2DPointArray, these will be placed on the end of the T2DPointArray.
SortATPAFromFirstPoint(ATPA, Point(MSCX, MSCY));

k := High(atpa); //Find the highest element.
For I:= 0 to k do
begin
//We need to click something, but we got loads of points what now?
//We find the middle of the TPointArray, how?
//Count up all the x and y values and divide them by the length of the TPointArray.
//This will leave us a point thats in the middle of the TPA!
//In this case, the TPA is an sub-array of the T2DPointArray.
MiddleTPAEx(ATPA[i], x, y);
MMouse(x, y, 1, 1);//We move the mouse to the middle of the TPointArray, to check wether its the tree of the Servant.
Wait(150 + Random(70));
GetMousePos(x, y);
if (Pos('ervan', rs_GetUpText) <= 0) then //Its no the servant, lets continue with the for loop!
Continue;
//Yay its the servant! We can click it!
Mouse(x, y, 0, 0, True);
FFlag(0);
ColorToleranceSpeed(cts);
Result := True;
Exit;
end;
end;

To be honest, to really learn them just try, try..

TPA's the hard part.

Ofcourse right here we've been using some "simple" functions using the WizzyPlugin.. So now you want to know how the plugin does it?
Lets start with some debugging of TPA's.

Say you want to highlight all the parts of an TPA. Well we will make a function for that.
procedure DrawTPA(TPA : TPointArray);   //Put in a TPA, and it returns you a debug image with the found points :-)
var
TempBitmap,I,LengthA : integer;
Size : TBox;
begin;
//The TPA contains a shitload of points. Say we want to get the
//bounds of the TPA, meaning you get the lowest x and y value, and the highest x and y value.
//Then save this as an TBox.
Size := GetTPABounds(TPA);
//Create a bitmap for the area defined by the box.
TempBitmap := BitmapFromString(Size.x2-Size.x1+1,Size.y2-Size.y1+1,'');
//Copy the client onto this bitmap.
CopyClientToBitmap(TempBitmap,Size.x1,Size.y1,Size.x2,Size.y2);
LengthA := High(TPA);
For I:= 0 to LengthA do
FastSetPixel(TempBitmap,TPA[I].x-Size.x1,TPA[I].y-Size.y1,clred);//Loop through the whole TPA and make every pixel thats found red.
DisplayDebugImgWindow(Size.x2-Size.x1+1,Size.y2-Size.y1+1);//Display Debug image
SafeDrawBitmap(TempBitmap,GetDebugCanvas,0,0);//Draw onto debug image.
FreeBitmap(TempBitmap);//Free, always free!
end;

This is comming in handy already!
Now something more challenging, how about the FindTalk?
First I'll explain how it works:
1) First you create a bitmap of the nick.
2) You find all the white points of this bitmap and save them in a TPA.
3) Find all the yellow colors (the text color) and then compare this points with the points of the bitmap, and when they match your nick is found.. Easy as that..

Code plox? First I use a technique that I use quite often when doing some TPA finding. I create a 2D Boolean array, with the size of the TPA. Then I pretend that the first array is the X and the second is the Y. Then I set all the TPA points to true.. Pseudo code is like this
program New;
var
d: Array of Array of Boolean;
begin
d[x][y] := True;
end.

Here we go
function FindTPAinTPA(SearchTPA, TotalTPA: TPointArray; var Matches: TPointArray): Boolean; stdcall;
var
Len, I,II,LenSearch,xOff,yOff : integer;
tx,ty,MatchCount : integer;
Screen : Array of Array of Boolean;
ScreenBox,SearchBox : TBox;
begin;
Result := False; //Initalize.
Len := High(TotalTPA);//Get length of total.
LenSearch := High(SearchTPA);
if LenSearch < 0 then Exit; //Failsafe.
if Len < LenSearch then Exit;//Failsafe.
MatchCount := 0;
ScreenBox := GetTPABounds(TotalTPA); //We get the bounds of the total TPA (the yellow color)
SearchBox := GetTPABounds(SearchTPA); //Bounds of the searchtpa (your nick).
try
SetLength(Screen,ScreenBox.x2 + 1,ScreenBox.y2 + 1); //This is an delpi function in scar not possible..
//What it does is like, setlength(Screen,x2 + 1);
//Then it loops from 0 to x2 + 1 and sets the length of that array of y2 + 1..
// For I := 0 to ScreenBox.x2 + 1 do
// SetLenth(Screen[I], ScreenBox.y2 + 1);
//Like that.
except
Exit;
end;
//The following part is to be sure that the points of the bitmap are adjusted so that the first point has 0,0.
//Other wise its not really working, not very important.
//You can imagene it like this..
//Before:----------- After: -----------
// | | |xxx |
// | xxx | |x x |
// | x x | | |
// ----------- -----------
if (SearchBox.x1 > 0) or (SearchBox.y1 > 0) then
begin;
for I := 0 to LenSearch do
begin;
SearchTPA[I].x := SearchTPA[I].x - SearchBox.x1;//Adjust all points so they
SearchTPA[I].y := SearchTPA[I].y - SearchBox.y1;
end;
SearchBox.x2 := SearchBox.x2 - SearchBox.x1;
SearchBox.y2 := SearchBox.y2 - SearchBox.y1;
SearchBox.x1 := 0;
SearchBox.y1 := 0;
end;
//This part is to find the xOff and yOff not really important.
//Its used so you know the offset from y at x = 0, and the x offset at y = 0.
xOff := SearchBox.x2;
yOff := SearchBox.y2;
for I := 0 to LenSearch do
begin;
if (SearchTPA[I].x = 0) and (SearchTPA[I].y < yOff) then
yOff := SearchTPA[I].y;
if (SearchTPA[I].y = 0) and (SearchTPA[I].x < xOff) then
xOff := SearchTPA[I].x;
end;
//We set all the found points to true in the 2D Bool array.
for I := 0 to Len do
Screen[TotalTPA[I].x][TotalTPA[I].y] := True;
//The big work!
for I := 0 to Len do
begin;
tx := TotalTPA[I].x - xOff; //We adjust the point with the offsets. (Yellow points adjusted with the offset from the searchtpa.
ty := TotalTPA[I].y;// - yOff;
if tx > 0 then //If the points are > 0.
if ty > 0 then
//We check if the points + the searchtpa.x2 are inside the screenbox, otherwise its not an possible match.
if ((SearchBox.x2 + tx) <= ScreenBox.x2) and ((SearchBox.y2 + ty) <= ScreenBox.y2) then
begin;
//Its inside the screen this might be the one we are looking for!
//We loop through the whole nick to find out wether its matching the yellow points correctly.
For II := 0 to LenSearch do
if Screen[tx + SearchTPA[II].x ][ty + SearchTPA[II].y] = False then
Break;
//Its matching! Yay add it to the matches!
if II > LenSearch then
begin;
MatchCount := MatchCount + 1;
SetLength(Matches,MatchCount);
Matches[MatchCount - 1].x := TotalTPA[I].x;
Matches[MatchCount - 1].y := TotalTPA[I].y;
end;
end;
end;
if (MatchCount > 0) then
Result := True;
end;

~Raymond.

Correct my grammer if needed, if I did something wrong or you need more help just post:D.

Dumpin

2008-03-16 10:36:47

nice, just what i needed, TPA's always failed for me :)

drizzt

2008-03-16 10:46:52

From: mastaraymond

Correct my grammer if needed, if I did something wrong or you need more help just post:D.


haha, only place i saw any *grammar* mistakes was it that sentence. great tutorial, very in depth and explained beatifully

[S]paz

2008-03-16 10:55:04

i think i finally understand, kinda :) need to test and try these out, thanks Ray
they always seemed to never find the objects i wanted thanks

~Spaz

Harry

2008-03-16 10:57:27

Your 'grammer' is fine IMO :)

Looks nice… I might make my script use this instead of FindObjCustom :) Thanks Ray-Ray.

n3ss3s

2008-03-16 14:24:31

That's a nice tutorial, propably better than mine :rolleyes:

You told us to correct mistakes… -

TPA = Kill With Powarrr!!


Ray, records work like this:


Program Hoi;

Type
aRecord = record
a, b : Integer;
End;

Type
aRecordArray = Array of aRecord;
begin
end.

Also maybe tell them that TPointArray declaration doesn't need zeh record, otherwise they'll be getting whole lotta errors :rolleyes:

Gj…

Negaal

2008-03-16 14:34:29

Very nice tutorial…ahh, nothing much to say, just had to leave comment so Ray feels good:p

Dumpin

2008-03-16 14:35:56

From: n3ss3s


TPA = Kill With Powarrr!!

Gj…


yes!! i found a spelling mistake!

Wizzup?

2008-03-16 18:43:47

Thanks, this really helped me understand TPA's. :)

bullzeye95

2008-03-16 19:54:50

From: Wizzup?
Thanks, this really helped me understand TPA's. :)


ya me 2. btw mastraymund pls add me on msn i wnt 2 no hwo 2 use smart. ty


(nice tutorial :))

lordsaturn

2008-03-16 20:08:01

From: Wizzup?
Thanks, this really helped me understand TPA's. :)


Rofl.

HellBoyz

2008-03-16 23:59:29

Nice tut… Bookmark! lol

mastaraymond

2008-03-17 10:21:22

From: n3ss3s
That's a nice tutorial, propably better than mine :rolleyes:

You told us to correct mistakes… -

TPA = Kill With Powarrr!!


Ray, records work like this:


Program Hoi;

Type
aRecord = record
a, b : Integer;
End;

Type
aRecordArray = Array of aRecord;
begin
end.

Also maybe tell them that TPointArray declaration doesn't need zeh record, otherwise they'll be getting whole lotta errors :rolleyes:

Gj…

Meh, My fault :p.
From: Wizzup?
Thanks, this really helped me understand TPA's. :)

At least people responded to this tutorial.. xD Cannot say that of yours :p

EvilChicken!

2008-03-17 15:45:15

From: n3ss3s

You told us to correct mistakes… -

TPA = Kill With Powarrr!!


No, thats CIA.

Anyway Ray - VERY nice tut! I loved the BobServant example and TPA's actually work for me now!

ONE slight thing. "TPAtoATPAEx(TPA, W, H);". W and H are in pixels? Any comments on this one? Because I really want to know what I need to do wit this one, so far I've been guessing the two integers (varying from 20-30 both) and I kinda want to know exactly what to fill in there to make it mostly efficient..

I'm truly sorry for my bad grammar.

Edit: Thanks, bullzeye.
E2: And huge thanks Ray.

bullzeye95

2008-03-17 15:49:23

Yes, it is in pixels.

mastaraymond

2008-03-17 17:17:21

From: EvilChicken!
No, thats CIA.

Anyway Ray - VERY nice tut! I loved the BobServant example and TPA's actually work for me now!

ONE slight thing. "TPAtoATPAEx(TPA, W, H);". W and H are in pixels? Any comments on this one? Because I really want to know what I need to do wit this one, so far I've been guessing the two integers (varying from 20-30 both) and I kinda want to know exactly what to fill in there to make it mostly efficient..

I'm truly sorry for my bad grammar.

Edit: Thanks, bullzeye.

They are in pixels.. When you pick to points, say you pick the left upper corner of a rock and the right under corner, then you get the W and H between that points.. (Y2 - Y1 = H; X2 - X1 = W)..

faster789

2008-03-26 00:22:29

lol u shurr this not advanced lol looks good but i need to learn way mor to understand..

mastaraymond

2008-04-17 11:26:39

From: faster789
lol u shurr this not advanced lol looks good but i need to learn way mor to understand..

Its not really advanced in my opinion. You just must know the basics of programming and it should be all fine :p.

Heavenzeyez1

2008-08-28 12:16:07

procedure DrawTPA(TPA : TPointArray);
Amazing , thanks .
~Eerik~