PDA

View Full Version : ObjectFinding using TPointArrays



Negaal
02-16-2008, 02:57 PM
Hello...

...Small tutorial to reduce questions about TPointArrays

If you don't know what arrays are then I suggest learning them before reading this, same for "for .. to .. do" statement

First, few explanations:

FindColorsTolerance - Like FindColorTolerance, but instead of finding one coord of that color, it find all coords of that color

FindColorsToleranceSpiral - Like FindColorsTolerance, but instead of starting scanning from 0(x), 0(y), it starts scanning from x and y.

Length/GetArrayLength - They're same, gets length of Array.

SetArrayLength/SetLength - They're same. To set length arrays length you have to choose one of them, if you get "Out of range error", then it means you have forgot to set arrays length, or you haven't filled array with any data.

TPoint - It means 2 values stored in 1 variable(x and y), example


program New;
var
ThePoint : Tpoint;
begin
ThePoint.x := 10;
ThePoint.y := 25;
writeln('X is '+inttostr(thepoint.x)+', Y is '+inttostr(thepoint.y));
MoveMouseSmooth(thepoint.x,thepoint.y);
end.


See, when they're seperated, they're basically 2 different integers...you can hold data in them and make to x and y later, like this

x := ThePoint.x;
y := ThePoint.y;


TPointArray - you can declare it as "Array of TPoint" aswell, but in further it's called as TPointArray, so yeah, basically it array of TPoints.
See the example.

program New;
var
ThePoints : TpointArray;
i : integer;
begin
SetLength(ThePoints, 3);

ThePoints[0].x := 10;
ThePoints[0].y := 25;

ThePoints[1].x := 156;
ThePoints[1].y := 90;

ThePoints[2].x := 500;
ThePoints[2].y := 276;

for i:=0 to 2 do
begin
writeln('X['+inttostr(i)+'] is '+inttostr(thepoints[i].x)+', Y['+inttostr(i)+'] is '+inttostr(thepoints[i].y));
MoveMouseSmooth(thepoints[i].x,thepoints[i].y);
Wait(1000);
end;
end.

Example above has 3 TPoints, we moved our mouse trough them.

Q: Could it be possible to use "Array of integer" for x and y?
A: No, see the the function, it's built in to scar...

FindColorsTolerance(var Points: TPointArray; Color, xs, ys, xe, ye, Tolerance: Integer): Boolean;
It needs to be TPointArray.

So lets use it.
Open up your MS Paint and do few red dots...

Now, run this

program New;
var
TPA : TpointArray;
i, h, w, HighestIndex : integer;
begin
GetClientDimensions(W, H);
FindColorsTolerance(TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
HighestIndex := High(TPA);
for i:=0 to HighestIndex do
begin
MoveMouseSmooth(TPA[i].x, TPA[i].y);
Wait(100);
end;
end;
end.


It moves trough every point it found.

HighestIndex and High(TPA), array starts from 0 and ends with highest indexnumber. So basically when we have 3 components on our array,
then arrays length is 3, but since arrays starts with 0 then highest indexnumber is 2, so you can choose between 2 commands:
"GetArrayLength(x) - 1"
or
"High(x)"

But why we did
"HighestIndex := High(TPA);"
instead doing it directly like this
for i:=0 to High(TPA) do

Answer is speed, if it have to get arrays length (- 1) every time it takes more time, so we better store it in integer.

Also here is example using FindColorsSpiralTolerance

program New;
var
TPA : TpointArray;
i, h, w, HighestIndex : integer;
begin
GetClientDimensions(W, H);
FindColorsSpiralTolerance(W/2, H/2, TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
HighestIndex := High(TPA);
for i:=0 to HighestIndex do
begin
MoveMouseSmooth(TPA[i].x, TPA[i].y);
Wait(100);
end;
end;
end.

Only difference is that it starts scanning from x and y, what are center of client in this case.

Now, we don't want to move through every pixel, ain't we?

*Note, many important TPA/ATPA functions comes from WizzyPlugin.

First, how to find middle of those points?
Simple, we use MiddleTPA or MiddleTPAEx.
Example if there are 2 points.
First point, x = 10, y = 20
Second point x = 20 y = 10
Middle of them is x = 15 and y = 15.

How to use it in script?

program New;
var
TPA : TpointArray;
i, h, w : integer;
MidPoint : TPoint;
begin
GetClientDimensions(W, H);
FindColorsSpiralTolerance(W/2, H/2, TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
MidPoint := MiddleTPA(TPA);
MoveMouseSmooth(MidPoint.x, MidPoint.y);
end;
end.


Or Using MiddleTPAex what I prefer :)

program New;
var
TPA : TpointArray;
i, h, w, x, y : integer;
MidPoint : TPoint;
begin
GetClientDimensions(W, H);
FindColorsSpiralTolerance(W/2, H/2, TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
MiddleTPAEx(TPA, x, y);
MoveMouseSmooth(x, y);
end;
end.


*Now this may look hard, what it isn't, I try to give my best explanation.*

Well, guess what, this isn't enough...
Lets say we have 2 monsters on main screen, we find middle of them, but middle of them is ground.
So best solution is to make TPoints to group, by sorting points by distance of closest point.
In that way we will get "Array of TPointArray", called "TPointArrayArray" in further.

We can use these.


{************************************************* ******************************
function SplitTPAEx(OldArray: TPointArray; w, h: Integer): TPointArrayArray;
By: Boreas and Nielsie95
Description: Splits a TPA into multiple TPAs so that each TPA contains
points that are in range of eachother (using w and h, width and height).
************************************************** *****************************}


{************************************************* ******************************
function SplitTPA(OldArray: TPointArray): array of TPointArray;
By: Boreas and Nielsie95
Description: Splits a TPA into multiple TPAs so that each TPA contains
points that are in range of eachother (using dist, Distance).
************************************************** *****************************}


{************************************************* ******************************
function TPAtoATPAEx(TPA: TPointArray; w, h: Integer): array of TPointArray;
By: Wizzup? and Nielsie95
Description: Turns an TPointArray into an Array Of TPointArray. Sort's the array
by distance. Points that are near to other points (using w and h, width and height),
are put into the TPointArray of Array of the 'overlapping' (a.k.a. First Point) Point.
************************************************** *****************************}


{************************************************* ******************************
function TPAtoATPA(TPA: TPointArray; Dist: Integer): array of TPointArray;
By: Wizzup?, Nielsie95 and killerdou
Description: Turns an TPointArray into an Array Of TPointArray. Sort's the array
by distance. Points that are near to other points (using Dist, Distance),
are put into the TPointArray of Array of the 'overlapping' (a.k.a. First Point) Point.
************************************************** *****************************}

You might get confused now, I put some comments into script


program New;
var
TPA : TpointArray;
ATPA : TPointArrayArray;
i, h, w, x, y, l : integer;
begin
GetClientDimensions(W, H);
FindColorsTolerance(TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
ATPA := SplitTPAEx(TPA, 25, 25); //Splits TPoint into groups with max distance of 25 pixels
L := High(ATPA)
for i := 0 to L do
if Length(ATPA[i]) > 0 then //ATPA[i] is basically a TPA.
begin
writeln('ATPA index = '+inttostr(i));
MiddleTPAEx(ATPA[i], x, y);
MoveMouseSmooth(x, y);
Wait(1000);
end;
end;
end.


Edit:
You might didn't understood TPAtoATPA or SplitTPA.
Then run this.
Try both. SplitTPA and TPAtoATPA.

program New; //Edited out from wizzup's DebugATPA/TPA

var
TPA : TpointArray;
ATPA : TPointArrayArray;
i, h, w, x, y, l : integer;

Function DebugATPA(aPoints: Array Of TPointArray; BmpName: String): Boolean;
Var
Width, Height, ClientBMP, I, L, Col, x2, y2 : Integer;
ClientBox : tbox;
Box : tbox;
Begin
Try
begin
for i := 0 to high(apoints) do
begin
clientbox := GetTPABounds(APoints[i]);
x2 := max(x2, clientbox.x2);
y2 := max(y2, clientbox.y2);
end;
width := x2+5;
height := y2+5;
DisplayDebugImgWindow(Width, Height);
ClientBMP := BitmapFromString(Width, Height, '');
CopyClientToBitmap(ClientBMP, 0, 0, width, height)
For I := 0 To High(aPoints) Do
Begin
case random(5) of
0 : col := clred;
1 : col := clblue;
2 : col := clblack;
3 : col := clgreen;
4 : col := clyellow;
end;
box := GetTPABounds(APoints[i]);
box.x1 := box.x1 - 3;
box.y1 := box.y1 - 3;
box.x2 := box.x2 + 3;
box.y2 := box.y2 + 3;
getdebugcanvas.Font.Size := 6;
FastDrawTransparent(box.x1+2, box.y1+2, CreateBitmapFromText('ATPA['+inttostr(i)+']', 2, clwhite),clientbmp);
For L := box.x1 To box.x2 Do
begin
FastSetPixel(ClientBMP, l, box.y1, Col);
FastSetPixel(ClientBMP, l, box.y2, Col);
end;
For L := box.y1 To box.y2 Do
begin
FastSetPixel(ClientBMP, box.x1, l, Col);
FastSetPixel(ClientBMP, box.x2, l, Col);
end;
End;
SafeDrawBitmap(ClientBMP, GetDebugCanvas, 0, 0);
DisplayDebugImgWindow(Width, Height);
If BmpName <> '' Then
SaveBitmap(ClientBMP, ScriptPath + BmpName + '.bmp');
FreeBitmap(ClientBMP);
End;
Except
FreeBitmap(ClientBMP);
End;
Result := True;
End;


begin
GetClientDimensions(W, H);
FindColorsTolerance(TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
ATPA := SplitTPA(TPA, 10); //Try both. SplitTPA and TPAtoATPA.
debugatpa(atpa, '');
L := High(ATPA)
for i := 0 to L do
if Length(ATPA[i]) > 0 then //ATPA[i] is basically a TPA.
begin
writeln('ATPA index = '+inttostr(i));
MiddleTPAEx(ATPA[i], x, y);
MoveMouseSmooth(x, y);
Wait(1000);
end;
end;
end.

if you run this script on this picture then it moves the mouse to blue X'es.
http://i30.tinypic.com/2q9f7sz.jpg

Order may seem confusing? right?
We want to find nearest? or sort them somehow?

We might want to use following sorters, or if we use FindColorsSpiralTolerance instead of FindColorsTolerance, you'll not need SortATPAFrom or SortTPAFrom and so on...(Thanks n3ss3s)


{************************************************* ******************************
procedure SortATPAFrom(var a: array of TPointArray; From: TPoint);
By: Nielsie95 and MastaRaymond
Description: Sorts all points in the Array of TPointArray by distance from
Point From.
************************************************** *****************************}


{************************************************* ******************************
rocedure SortATPAFromFirstPoint(var a: array of TPointArray; From: TPoint);
By: Nielsie95 and MastaRaymond
Description: Sorts the Array of TPointArray by distance between the first point
in the array and Point From.
************************************************** *****************************}

{************************************************* ******************************
procedure SortTPAFrom(var a: TPointArray; From: TPoint);
By: Nielsie95 and MastaRaymond
Description: Sorts the TPointArray by Distance from Point From.
************************************************** *****************************}


{************************************************* ******************************
procedure SortATPAFromSize(var a: array of TPointArray; Size: Integer; CloseFirst: Boolean);
By: Nielsie95
Description: Sorts the Array of TPointArray by difference from Size.
************************************************** *****************************}


{************************************************* ******************************
procedure SortATPASize(var a: array of TPointArray; BigFirst: Boolean);
By: Nielsie95
Description: BubbleSorts the Array of TPointArray by ArraySize.
************************************************** *****************************}



Lets use one of them in script:

program New;
var
TPA : TpointArray;
ATPA : TPointArrayArray;
i, h, w, x, y, l : integer;
SortPoint : TPoint;
begin
GetClientDimensions(W, H);
Sortpoint.x := w / 2;
Sortpoint.y := h / 2;
FindColorsTolerance(TPA, 255, 0, 0, W, H, 0);
if Length(TPA) > 0 then
begin
ATPA := SplitTPAEx(TPA, 5, 5);
SortATPAFromFirstPoint(ATPA, SortPoint)
L := High(ATPA)
for i := 0 to L do
if Length(ATPA[i]) > 0 then
begin
MiddleTPAEx(ATPA[i], x, y);
writeln('ATPA index = '+inttostr(i)+', Distance = '+inttostr(distance(x,y,sortpoint.x,sortpoint.y))) ;
MoveMouseSmooth(x, y);
Wait(1000);
end;
end;
end.


Try running the script on this image.
http://i32.tinypic.com/669c3l.jpg


...We nicely sorted out closest objects.(Actually the wizzyplugin did :D)

Declaring SortPoint every time isn't cool?
Yup.
So instead of

Sortpoint.x := MSCX;
Sortpoint.y := MSCY;
SortATPAFromFirstPoint(ATPA, SortPoint);

you may want to do

SortATPAFromFirstPoint(ATPA, IntToPoint(MSCX, MSCY));


So, how should it look in RuneScape to use?

function FindSomething(var x, y, Color, MinPoints : integer; Text : String) : Boolean;
var
TPA : TPointArray;
ATPA : TPointArrayArray;
I, L : integer;
begin
FindColorsTolerance(TPA, Color, MSX1, MSY1, MSX2, MSY2, 12);
if Length(ATPA[i]) >= MinPoints then
begin
ATPA := SplitTPAEx(TPA, 10, 10);
SortATPAFromFirstPoint(ATPA, IntToPoint(MSCX, MSCY));
L := High(ATPA)
For i := 0 to L do
if Length(ATPA[i]) >= MinPoints then
begin
MiddleTPAEx(ATPA[i], x, y);
MMouse(x - 5, y - 5, 11, 11);
Wait(50 + random(50));
if IsUpText(Text) then
begin
Getmousepos(x, y);
Result := True;
status('Object found!');
Exit;
end;
end;
end;
end;


Important note,
TPointArrayArray/Array of TPointArray is changed to T2DPointArray in rev 14.

Hope this helped;)

n3ss3s
02-16-2008, 03:05 PM
Good job, btw -

a) You knew that FindColorsTolerance searches from (x1, y1) to (x2, y2)? So, either of the "2" or "3" would be "1", and the "1" would be "3".

b)

...We nicely sorted out closest objects.(Actually the wizzyplugin did ) Why don't you also tell them that if you use FindColorsSpiralTolerance instead of FindColorsTolerance, you need no SortATPAFrom or SortTPAFrom :)


;)

Negaal
02-16-2008, 03:08 PM
Good job, btw -

a) You knew that FindColorsTolerance searches from (x1, y1) to (x2, y2)? So, either of the "2" or "3" would be "1", and the "1" would be "3".

b) Why don't you also tell them that if you use FindColorsSpiralTolerance instead of FindColorsTolerance, you need no SortATPAFrom or SortTPAFrom :)


;)
Thanks
b) I planned to say it, but forgot. Added it.

nielsie95
02-16-2008, 03:21 PM
ATPA := SplitTPAEx(TPA, 25, 25); //Splits TPoint into groups with max distance of 25 pixels

SplitTPA splits points into groups with a max distance between all the points in the array.
TPAtoATPA splits points into groups with a max distance based on the first point.

Like this:
http://img403.imageshack.us/img403/9875/atpauitlegag8.png

Negaal
02-16-2008, 04:25 PM
SplitTPA splits points into groups with a max distance between all the points in the array.
TPAtoATPA splits points into groups with a max distance based on the first point.

Like this:
http://img403.imageshack.us/img403/9875/atpauitlegag8.png

Let's say that english isn't my primary language:rolleyes:
If I read some info from wizzyplugin I often get confused...so forgive me if i can't understand/express myself clearly.

Yea there are mistakes in my tutorial, please rate it! :)

Edit:
TPointArray of Array of the 'overlapping' (a.k.a. First Point) Point.


It's like wdf!? for me...

gerauchert
02-16-2008, 05:41 PM
Nice tut! I knew most of this, but you cleared up some of the finer details for me. Thanks ;)

stampede10343
02-19-2008, 01:00 PM
thanks a bunch this will help me a lot, n3ss3s cleared some stuff up but this made it pretty crystal ;) thanks

Raskolnikov
02-20-2008, 12:03 AM
Wow! I learned soooo much about TPAs from this. Thanks :D

Cut em2 it

Scaper
07-31-2008, 08:55 PM
nice tut learnt me a few extra thing good job rep++

lordsaturn
08-29-2008, 02:35 AM
Thank you for this tut, helped me understand ATPA functions better. =]

Tniffoc
09-09-2008, 03:19 AM
4-5 month gravedig by Scaper!

But, this tut is so good and usefull that I think it's ok. :)

Negaal
10-15-2008, 03:34 PM
There is no gravedigging in tutorials section.