Hello, I think I've written a very useful function for finding objects. Basically, you only need to pick a few colors on the object and my function will return an array of points on the screen where those colors are clustered together.
For example, I took this function and used it in a script I'm making to open the bank using three pre-picked colors on the banker. It works perfectly (as far as I know). For most objects that have more than one color, finding them will be as simple as color picking 2-4 colors that make up a unique cluster.
Here's a description of the arguments:
SCAR Code:
//Searches the rectangle x1,y1 to x2,y2 for the colors. w and h give width and height of the rect used to find nearby colors
//tol is an array of the tolerence on each color (where the first tol integer corresponds to the first color in the color array, etc.)
//xColorOffset is an array that offsets all the points of the corresponding color horizontally, and yColorOffset something similarly vertically
//For example, in order to find the smelting furnace using the two colors [5204965, 3223859], I will need to shift the points found with the first orange color (the heat coming off) so that they are clustered closer to the other gray color of the actual furnace.
//so for xColorOffset I might put [-15, 0] meaning the first color is offset by -15 horizontally.
And here's the actual code with comments (warning, it's a bit complicated at times):
SCAR Code:
function FindObjColorCluster(colors:TIntegerArray; xColorOffset, yColorOffset:TIntegerArray; x1, y1, x2, y2, w, h:Integer; tol:TIntegerArray):TPointArray;
var
i, j, k:Integer;
numColors, numColorsLessOne, clusterIndex:Integer;
colorsPoints, colorClusters:T2DPointArray;
colorIndex:TIntegerArray;
clusterMidPoints:TPointArray;
closeColorFound, doneFindingClusters:Boolean;
begin
if(not LoggedIn) then Exit;
numColors := Length(colors);
if(numColors <= 0) or (Length(tol) <> numColors) then
Exit;
numColorsLessOne := numColors - 1;
SetLength(colorsPoints, numColors);
for i := 0 to numColorsLessOne do // for each color...
begin
FindColorsTolerance(colorsPoints[i], colors[i], x1, y1, x2, y2, tol[i]);
RAaSTPAEx(colorsPoints[i], w, h);
end;
SetLength(colorIndex, numColors);
SetLength(colorClusters, Length(colorsPoints[0]));
for i := 0 to numColorsLessOne do
colorIndex[i] := High(colorsPoints[i]);
for i := 0 to High(colorClusters) do
SetLength(colorClusters[i], numColors);
clusterIndex := 0;
//add offsets
if(Length(xColorOffset) > 0) then
for i := 0 to numColorsLessOne do
for j := 0 to colorIndex[i] do
IncEx(colorsPoints[i][j].x, xColorOffset[i]);
if(Length(yColorOffset) > 0) then
for i := 0 to numColorsLessOne do
for j := 0 to colorIndex[i] do
IncEx(colorsPoints[i][j].y, yColorOffset[i]);
if(numColors = 1) then
begin
Result := colorsPoints[0];
Exit;
end;
//at this point we've got an ATPA with the points organized by color
//next, find common clusters where one point from each color is inside one rectangle
for i := 0 to colorIndex[0] do //for each point of the first color
begin
colorClusters[clusterIndex][0] := colorsPoints[0][i];
for j := 1 to numColorsLessOne do //for each color after the first
begin
closeColorFound := False;
for k := 0 to colorIndex[j] do //for each point in each color
begin
if(PointIsNearby(colorsPoints[0][i], colorsPoints[j][k], w, h)) then //if first color point is close to this color point, add to colorCluster
begin
closeColorFound := True;
colorClusters[clusterIndex][j] := colorsPoints[j][k];
TSwap(colorsPoints[j][k], colorsPoints[j][colorIndex[j]]); //these lines stop this point from being compared again to
Dec(colorIndex[j]); //another of the first points, saving some time
if(colorIndex[j] < 0) then
doneFindingClusters := True;
break;
end;
end;
if(not closeColorFound) then
break;
if(j = numColorsLessOne) then //if this is last color, that means we have found a color cluster!
Inc(clusterIndex);
end;
if(doneFindingClusters) then
break;
end;
//almost done... now we have an ATPA with clusters of points and each cluster containing one point of each color,
//so, get the middle point of each of these clusters, and return them in a TPA
SetLength(clusterMidPoints, clusterIndex);
for i := 0 to clusterIndex - 1 do
MiddleTPAEx(colorClusters[i], clusterMidPoints[i].x, clusterMidPoints[i].y);
Result := clusterMidPoints;
end;
And this is a small function I used in the above code:
SCAR Code:
function PointIsNearby(p1, p2:TPoint; w, h:Integer):Boolean; //is p2 within the rectangle given by w, h and centered on p1
begin
Result := (p2.x > p1.x - w/2) and (p2.x < p1.x + w/2) and (p2.y > p1.y - h/2) and (p2.y < p1.y + h/2);
end;
I'm surprised that there isn't a function like PointIsNearby. Maybe there is and I just didn't see it. Oh well.
Anyway, in case you're interested, here's the function I wrote to open the bank:
SCAR Code:
function MyOpenBank:Boolean;
var
i:Integer;
thePoints:TPointArray;
bankColors:TIntegerArray;
begin
bankColors := [16642813, 7303028, 939362];
thePoints := FindObjColorCluster(bankColors, [], [], MSX1, MSY1, MSX2, MSY2, 20, 20, [9, 9, 9]);
if(Length(thePoints) > 0) then
begin
for i := 0 to Length(thePoints) - 1 do
if(thePoints[i].y < thePoints[0].y) then
TSwap(thePoints[0],thePoints[i]);
if(MenuSelect(thePoints[0].x, thePoints[0].y , 'nk Ba')) then
Result := True;
end;
end;
The part with the for loop and swapping is just to get the banker which is highest up (lowest y value). And the function MenuSelect is another function I wrote, but I don't think it really needs an explanation.
And here's a slightly faster version of FindObjColorCluster which is the same except it returns the first TPoint it finds instead of going on to find every point and returning them in an array. This one also has no offsets and one tol value which it uses for every color, so it's much simpler to use.
SCAR Code:
function FindObjColorCluster2(colors:TIntegerArray; x1, y1, x2, y2, w, h, tol:Integer):TPoint;
var
numColors, numColorsLessOne, i, j, k, l:Integer;
colorsPoints:T2DPointArray;
colorCluster:TPointArray;
colorIndex:TIntegerArray;
closeColorFound, doneFindingCluster:Boolean;
thePoint:TPoint;
begin
if(not LoggedIn) then Exit;
numColors := Length(colors);
if(numColors <= 0) then
Exit;
numColorsLessOne := numColors - 1;
SetLength(colorsPoints, numColors);
SetLength(colorCluster, numColors);
for i := 0 to numColorsLessOne do
begin
FindColorsTolerance(colorsPoints[i], colors[i], x1, y1, x2, y2, tol);
RAaSTPAEx(colorsPoints[i], w, h);
end;
if(numColors = 1) then
begin
Result := colorsPoints[0][0];
Exit;
end;
SetLength(colorIndex, numColors);
for i := 0 to numColorsLessOne do
colorIndex[i] := High(colorsPoints[i]);
for j := 0 to colorIndex[0] do
begin
colorCluster[0] := colorsPoints[0][j];
for k := 1 to numColorsLessOne do
begin
closeColorFound := False;
for l := 0 to colorIndex[k] do
begin
if(PointIsNearby(colorsPoints[0][j], colorsPoints[k][l], w, h)) then
begin
closeColorFound := True;
colorCluster[k] := colorsPoints[k][l];
TSwap(colorsPoints[k][l], colorsPoints[k][colorIndex[k]]);
Dec(colorIndex[k]);
if(colorIndex[k] < 0) then
doneFindingCluster := True;
break;
end;
end;
if(not closeColorFound) then
break;
if(k = numColorsLessOne) then
begin
MiddleTPAEx(colorCluster, thePoint.x, thePoint.y);
Result := thePoint;
Exit;
end;
end;
if(doneFindingCluster) then
break;
end;
end;
Knowing how you do things here, you might want to change it to return a boolean, and add an argument for a TPoint to output the point found. (just let me know, and I'll change it).
Finally, I've attached a file with the functions so you can test them.
Hopefully you'll find my function as useful as I did. And thanks for the wonderful library to work with; I don't know what I'd do without RAaSTPAEx. All I can say is I'm glad I joined this community.
Jahooma
Edit: updated code a little with suggestions from Narcle.
I would like to say that the code is fairly unclear in how it does its stuff, but that's because I've optimized it quite a bit. For example the use of the variable colorIndex in the code is quite difficult to explain and even harder to just figure out by looking at it. A short explanation is that it holds the high value that the for loops should search up to for each color. When points are put into the colorCluster variable, they are excluded from begin matched again with other points to make other clusters, so they are swapped to the back of the array, and the colorIndex[k] is decreased by 1.